summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm.txt10
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dp.txt52
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt13
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dsi.txt2
-rw-r--r--Documentation/devicetree/bindings/input/qpnp-power-on.txt2
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt8
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt12
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt8
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt4
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt13
-rw-r--r--Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt4
-rw-r--r--Documentation/kernel-parameters.txt5
-rw-r--r--Documentation/networking/ip-sysctl.txt13
-rw-r--r--Documentation/networking/netlink_mmap.txt332
-rw-r--r--Makefile10
-rw-r--r--android/configs/android-base.cfg25
-rw-r--r--android/configs/android-recommended.cfg8
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/boot/dts/qcom/Makefile49
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi45
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-cdp.dts (renamed from arch/arm/boot/dts/qcom/apq8998-cdp.dts)8
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-mtp.dts (renamed from arch/arm/boot/dts/qcom/apq8998-mtp.dts)8
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts (renamed from arch/arm/boot/dts/qcom/apq8998-v2.1-cdp.dts)8
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts (renamed from arch/arm/boot/dts/qcom/apq8998-v2-mtp.dts)8
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts (renamed from arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts)4
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts (renamed from arch/arm/boot/dts/qcom/apq8998-v2-qrd.dts)8
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts (renamed from arch/arm/boot/dts/qcom/apq8998-v2-cdp.dts)8
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts (renamed from arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts)6
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts (renamed from arch/arm/boot/dts/qcom/apq8998-v2.1-mtp.dts)8
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts (renamed from arch/arm/boot/dts/qcom/apq8998-v2.1-qrd.dts)8
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2.1.dtsi (renamed from arch/arm/boot/dts/qcom/apq8998-v2.1.dtsi)4
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2.dtsi (renamed from arch/arm/boot/dts/qcom/apq8998-v2.dtsi)4
-rw-r--r--arch/arm/boot/dts/qcom/apq8098.dtsi (renamed from arch/arm/boot/dts/qcom/apq8998.dtsi)4
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm660.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm660l.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi39
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi45
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-gpu.dtsi9
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi45
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-sde.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi264
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts28
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-cdp.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mdss.dtsi11
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts28
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mtp.dtsi5
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts27
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts27
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts27
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts28
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts28
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts28
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts51
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts28
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.1.dts23
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.dts23
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.dtsi112
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dts23
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts85
-rw-r--r--arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts56
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-cdp.dtsi27
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi91
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-mtp.dtsi16
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts7
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts7
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-qrd.dtsi19
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-regulator.dtsi68
-rw-r--r--arch/arm/boot/dts/qcom/sdm630.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-cdp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-common.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi50
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss.dtsi11
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mtp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-regulator.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/sdm660.dtsi1
-rw-r--r--arch/arm/boot/dts/sama5d2.dtsi35
-rw-r--r--arch/arm/configs/sdm660-perf_defconfig1
-rw-r--r--arch/arm/configs/sdm660_defconfig1
-rw-r--r--arch/arm/crypto/aes-ce-glue.c4
-rw-r--r--arch/arm/kernel/vdso.c13
-rw-r--r--arch/arm/mach-at91/pm.c18
-rw-r--r--arch/arm/mm/dma-mapping.c206
-rw-r--r--arch/arm/vdso/Makefile2
-rw-r--r--arch/arm64/Kconfig20
-rw-r--r--arch/arm64/Makefile12
-rw-r--r--arch/arm64/boot/dts/arm/juno-r1.dts28
-rw-r--r--arch/arm64/boot/dts/arm/juno-sched-energy.dtsi147
-rw-r--r--arch/arm64/boot/dts/arm/juno.dts36
-rw-r--r--arch/arm64/configs/msm-perf_defconfig6
-rw-r--r--arch/arm64/configs/msm_defconfig6
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig10
-rw-r--r--arch/arm64/configs/msmcortex_defconfig13
-rw-r--r--arch/arm64/configs/msmcortex_mediabox_defconfig2
-rw-r--r--arch/arm64/configs/sdm660-perf_defconfig2
-rw-r--r--arch/arm64/configs/sdm660_defconfig2
-rw-r--r--arch/arm64/crypto/Kconfig5
-rw-r--r--arch/arm64/crypto/Makefile3
-rw-r--r--arch/arm64/crypto/aes-glue.c4
-rw-r--r--arch/arm64/crypto/poly-hash-ce-core.S163
-rw-r--r--arch/arm64/crypto/poly-hash-ce-glue.c166
-rw-r--r--arch/arm64/include/asm/cache.h12
-rw-r--r--arch/arm64/include/asm/hw_breakpoint.h6
-rw-r--r--arch/arm64/include/asm/vdso_datapage.h8
-rw-r--r--arch/arm64/kernel/Makefile4
-rw-r--r--arch/arm64/kernel/asm-offsets.c6
-rw-r--r--arch/arm64/kernel/cpufeature.c6
-rw-r--r--arch/arm64/kernel/hw_breakpoint.c153
-rw-r--r--arch/arm64/kernel/ptrace.c7
-rw-r--r--arch/arm64/kernel/vdso.c12
-rw-r--r--arch/arm64/kernel/vdso/Makefile7
-rw-r--r--arch/arm64/kernel/vdso/gettimeofday.S340
-rw-r--r--arch/arm64/mm/fault.c1
-rw-r--r--arch/c6x/kernel/ptrace.c41
-rw-r--r--arch/h8300/kernel/ptrace.c8
-rw-r--r--arch/ia64/Makefile4
-rw-r--r--arch/metag/kernel/ptrace.c19
-rw-r--r--arch/mips/kernel/ptrace.c3
-rw-r--r--arch/powerpc/boot/zImage.lds.S1
-rw-r--r--arch/powerpc/kvm/emulate.c1
-rw-r--r--arch/s390/pci/pci_dma.c16
-rw-r--r--arch/sparc/kernel/ptrace_64.c2
-rw-r--r--arch/x86/crypto/ghash-clmulni-intel_glue.c26
-rw-r--r--arch/x86/entry/vdso/Makefile4
-rw-r--r--arch/x86/kernel/cpu/mshyperv.c24
-rw-r--r--arch/x86/kernel/cpu/perf_event.c4
-rw-r--r--arch/x86/kernel/head64.c1
-rw-r--r--arch/x86/mm/kasan_init_64.c1
-rw-r--r--arch/x86/pci/xen.c23
-rw-r--r--arch/x86/xen/spinlock.c6
-rw-r--r--block/scsi_ioctl.c3
-rw-r--r--build.config.goldfish.arm12
-rw-r--r--build.config.goldfish.arm6412
-rw-r--r--build.config.goldfish.mips11
-rw-r--r--build.config.goldfish.mips6411
-rw-r--r--build.config.goldfish.x8612
-rw-r--r--build.config.goldfish.x86_6412
-rw-r--r--crypto/Kconfig18
-rw-r--r--crypto/Makefile1
-rw-r--r--crypto/algif_hash.c2
-rw-r--r--crypto/blkcipher.c21
-rw-r--r--crypto/cryptd.c1
-rw-r--r--crypto/gf128mul.c171
-rw-r--r--crypto/heh.c1033
-rw-r--r--crypto/mcryptd.c1
-rw-r--r--crypto/shash.c8
-rw-r--r--crypto/testmgr.c15
-rw-r--r--crypto/testmgr.h194
-rw-r--r--drivers/acpi/acpi_video.c3
-rw-r--r--drivers/acpi/blacklist.c28
-rw-r--r--drivers/android/Kconfig2
-rw-r--r--drivers/android/binder.c392
-rw-r--r--drivers/base/power/wakeup.c4
-rw-r--r--drivers/bluetooth/btfm_slim_wcn3990.c24
-rw-r--r--drivers/char/Kconfig12
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/adsprpc.c99
-rw-r--r--drivers/char/diag/diag_masks.c4
-rw-r--r--drivers/char/diag/diag_memorydevice.c94
-rw-r--r--drivers/char/diag/diag_mux.c10
-rw-r--r--drivers/char/diag/diagchar.h19
-rw-r--r--drivers/char/diag/diagchar_core.c141
-rw-r--r--drivers/char/diag/diagfwd.c1
-rw-r--r--drivers/char/diag/diagfwd_cntl.c44
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c436
-rw-r--r--drivers/char/diag/diagfwd_peripheral.h2
-rw-r--r--drivers/char/msm_smd_pkt.c1397
-rw-r--r--drivers/char/tpm/tpm_tis.c2
-rw-r--r--drivers/clk/msm/clock-local2.c5
-rw-r--r--drivers/clk/msm/clock-mmss-8998.c1
-rw-r--r--drivers/clk/qcom/clk-cpu-osm.c68
-rw-r--r--drivers/clk/qcom/gcc-sdm660.c13
-rw-r--r--drivers/clk/qcom/mmcc-sdm660.c42
-rw-r--r--drivers/cpufreq/cpufreq.c8
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c8
-rw-r--r--drivers/cpuidle/lpm-levels.c20
-rw-r--r--drivers/crypto/msm/qcedev.c51
-rw-r--r--drivers/esoc/esoc_dev.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c1
-rw-r--r--drivers/gpu/drm/drm_edid.c809
-rw-r--r--drivers/gpu/drm/msm/Makefile2
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c66
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c1
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c727
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h132
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c267
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c227
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.xml.h14
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h2
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c10
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.c47
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.h3
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder.c28
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder.h11
-rw-r--r--drivers/gpu/drm/msm/sde_edid_parser.c512
-rw-r--r--drivers/gpu/drm/msm/sde_edid_parser.h148
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c7
-rw-r--r--drivers/gpu/msm/adreno_drawctxt.c1
-rw-r--r--drivers/gpu/msm/kgsl.c28
-rw-r--r--drivers/gpu/msm/kgsl_device.h1
-rw-r--r--drivers/hv/hv.c8
-rw-r--r--drivers/hv/hv_balloon.c4
-rw-r--r--drivers/hv/hyperv_vmbus.h2
-rw-r--r--drivers/hv/vmbus_drv.c8
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.c4
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c7
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c13
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c6
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c3
-rw-r--r--drivers/input/misc/cm109.c4
-rw-r--r--drivers/input/misc/hbtp_input.c125
-rw-r--r--drivers/input/misc/ims-pcu.c4
-rw-r--r--drivers/input/misc/qpnp-power-on.c61
-rw-r--r--drivers/input/misc/vl53L0/stmvl53l0-cci.h3
-rw-r--r--drivers/input/misc/vl53L0/stmvl53l0.h1
-rw-r--r--drivers/input/misc/vl53L0/stmvl53l0_module-cci.c18
-rw-r--r--drivers/input/misc/vl53L0/stmvl53l0_module.c9
-rw-r--r--drivers/input/misc/yealink.c4
-rw-r--r--drivers/input/mouse/elan_i2c_core.c20
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h7
-rw-r--r--drivers/input/tablet/hanwang.c3
-rw-r--r--drivers/input/tablet/kbtab.c3
-rw-r--r--drivers/input/touchscreen/sur40.c3
-rw-r--r--drivers/iommu/arm-smmu.c55
-rw-r--r--drivers/iommu/dma-mapping-fast.c38
-rw-r--r--drivers/iommu/intel-iommu.c2
-rw-r--r--drivers/iommu/io-pgtable-fast.c19
-rw-r--r--drivers/iommu/iommu-debug.c8
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c3
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c73
-rw-r--r--drivers/md/Kconfig17
-rw-r--r--drivers/md/Makefile4
-rw-r--r--drivers/md/dm-android-verity.c55
-rw-r--r--drivers/md/dm-verity-avb.c217
-rw-r--r--drivers/md/dm-verity-target.c6
-rw-r--r--drivers/md/dm-verity.h1
-rw-r--r--drivers/md/raid10.c22
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c106
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c7
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c335
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h6
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h8
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h7
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c68
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c20
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h1
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c30
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c4
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c9
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c100
-rw-r--r--drivers/media/platform/msm/vidc/msm_v4l2_vidc.c3
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c75
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c33
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.h1
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c118
-rw-r--r--drivers/mfd/wcd934x-regmap.c1
-rw-r--r--drivers/misc/Kconfig6
-rw-r--r--drivers/misc/Makefile2
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c34
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/usf.c13
-rw-r--r--drivers/misc/uid_cputime.c240
-rw-r--r--drivers/misc/uid_sys_stats.c463
-rw-r--r--drivers/mmc/core/mmc.c14
-rw-r--r--drivers/mmc/host/ushc.c3
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-common.h6
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dev.c20
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c102
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c6
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c15
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c2
-rw-r--r--drivers/net/ethernet/msm/msm_rmnet_mhi.c11
-rw-r--r--drivers/net/hyperv/netvsc_drv.c67
-rw-r--r--drivers/net/vrf.c3
-rw-r--r--drivers/net/vxlan.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h13
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c8
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c17
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c1
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c3
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c1
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c16
-rw-r--r--drivers/net/wireless/ath/wil6210/sysfs.c65
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h14
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c66
-rw-r--r--drivers/net/wireless/cnss_prealloc/cnss_prealloc.c1
-rw-r--r--drivers/net/wireless/wcnss/wcnss_wlan.c129
-rw-r--r--drivers/parport/share.c6
-rw-r--r--drivers/pci/host/pci-msm.c36
-rw-r--r--drivers/pci/iov.c70
-rw-r--r--drivers/pci/pci.c34
-rw-r--r--drivers/pci/pci.h7
-rw-r--r--drivers/pci/probe.c3
-rw-r--r--drivers/pci/rom.c5
-rw-r--r--drivers/pci/setup-res.c48
-rw-r--r--drivers/perf/arm_pmu.c84
-rw-r--r--drivers/pinctrl/intel/pinctrl-cherryview.c5
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm.c4
-rw-r--r--drivers/platform/msm/gsi/gsi_dbg.c17
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c20
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c34
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c21
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_rt.c13
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c40
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c17
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c1
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h8
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_rt.c8
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c79
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c32
-rw-r--r--drivers/platform/msm/mhi/mhi.h17
-rw-r--r--drivers/platform/msm/mhi/mhi_main.c28
-rw-r--r--drivers/platform/msm/mhi/mhi_ssr.c10
-rw-r--r--drivers/platform/msm/msm_11ad/msm_11ad.c35
-rw-r--r--drivers/power/reset/msm-poweroff.c50
-rw-r--r--drivers/power/supply/qcom/battery.c51
-rw-r--r--drivers/power/supply/qcom/fg-core.h29
-rw-r--r--drivers/power/supply/qcom/qpnp-fg-gen3.c163
-rw-r--r--drivers/power/supply/qcom/qpnp-qnovo.c37
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c55
-rw-r--r--drivers/power/supply/qcom/smb-lib.c818
-rw-r--r--drivers/power/supply/qcom/smb-lib.h19
-rw-r--r--drivers/power/supply/qcom/smb-reg.h10
-rw-r--r--drivers/power/supply/qcom/smb138x-charger.c258
-rw-r--r--drivers/regulator/qpnp-labibb-regulator.c51
-rw-r--r--drivers/regulator/qpnp-oledb-regulator.c78
-rw-r--r--drivers/s390/crypto/ap_bus.c3
-rw-r--r--drivers/s390/crypto/ap_bus.h1
-rw-r--r--drivers/scsi/cxlflash/common.h8
-rw-r--r--drivers/scsi/cxlflash/main.c2
-rw-r--r--drivers/scsi/libiscsi.c26
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c1
-rw-r--r--drivers/scsi/ufs/ufshcd.c7
-rw-r--r--drivers/soc/qcom/Kconfig17
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/common_log.c41
-rw-r--r--drivers/soc/qcom/cpuss_dump.c4
-rw-r--r--drivers/soc/qcom/dcc.c6
-rw-r--r--drivers/soc/qcom/glink.c9
-rw-r--r--drivers/soc/qcom/glink_smem_native_xprt.c9
-rw-r--r--drivers/soc/qcom/memory_dump_v2.c30
-rw-r--r--drivers/soc/qcom/msm_minidump.c371
-rw-r--r--drivers/soc/qcom/pil-msa.c10
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr_tal_glink.c2
-rw-r--r--drivers/soc/qcom/qdsp6v2/audio_notifier.c2
-rw-r--r--drivers/soc/qcom/qpnp-haptic.c92
-rw-r--r--drivers/soc/qcom/rpm-smd.c6
-rw-r--r--drivers/soc/qcom/scm.c4
-rw-r--r--drivers/soc/qcom/service-locator.c138
-rw-r--r--drivers/soc/qcom/service-notifier.c177
-rw-r--r--drivers/soc/qcom/socinfo.c44
-rw-r--r--drivers/soc/qcom/spcom.c39
-rw-r--r--drivers/soc/qcom/subsystem_restart.c105
-rw-r--r--drivers/soc/qcom/watchdog_v2.c18
-rw-r--r--drivers/spi/spi_qsd.c55
-rw-r--r--drivers/spmi/spmi-pmic-arb.c13
-rw-r--r--drivers/staging/android/ashmem.c3
-rw-r--r--drivers/staging/android/ion/ion.c85
-rw-r--r--drivers/staging/android/lowmemorykiller.c34
-rw-r--r--drivers/staging/android/sync.c5
-rw-r--r--drivers/target/target_core_pscsi.c47
-rw-r--r--drivers/target/target_core_sbc.c10
-rw-r--r--drivers/tty/serial/8250/8250_pci.c23
-rw-r--r--drivers/usb/class/usbtmc.c9
-rw-r--r--drivers/usb/core/config.c10
-rw-r--r--drivers/usb/core/hcd.c26
-rw-r--r--drivers/usb/core/hub.c10
-rw-r--r--drivers/usb/core/quirks.c8
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c1
-rw-r--r--drivers/usb/gadget/configfs.c2
-rw-r--r--drivers/usb/gadget/function/f_accessory.c37
-rw-r--r--drivers/usb/gadget/function/f_acm.c4
-rw-r--r--drivers/usb/gadget/function/f_ccid.h36
-rw-r--r--drivers/usb/gadget/function/f_fs.c43
-rw-r--r--drivers/usb/gadget/function/f_uvc.c2
-rw-r--r--drivers/usb/misc/idmouse.c3
-rw-r--r--drivers/usb/misc/lvstest.c4
-rw-r--r--drivers/usb/misc/uss720.c5
-rw-r--r--drivers/usb/musb/musb_cppi41.c23
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c15
-rw-r--r--drivers/usb/serial/option.c17
-rw-r--r--drivers/usb/serial/qcserial.c2
-rw-r--r--drivers/usb/wusbcore/wa-hc.c3
-rw-r--r--drivers/uwb/hwa-rc.c3
-rw-r--r--drivers/uwb/i1480/dfu/usb.c3
-rw-r--r--drivers/vfio/vfio_iommu_spapr_tce.c20
-rw-r--r--drivers/video/console/fbcon.c67
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_dba_utils.c54
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c58
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_debug_xlog.c23
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c464
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h117
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c443
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c72
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h21
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c112
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h6
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c90
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c164
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_status.c31
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h7
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp_1x.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h31
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_cdm.c13
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c44
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c18
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pipe.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c164
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c45
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c358
-rw-r--r--drivers/video/hdmi.c4
-rw-r--r--drivers/virtio/virtio_balloon.c2
-rw-r--r--drivers/xen/xen-acpi-processor.c34
-rw-r--r--fs/9p/acl.c2
-rw-r--r--fs/attr.c2
-rw-r--r--fs/dcache.c2
-rw-r--r--fs/exec.c2
-rw-r--r--fs/ext4/Kconfig1
-rw-r--r--fs/ext4/crypto_fname.c3
-rw-r--r--fs/ext4/crypto_key.c99
-rw-r--r--fs/ext4/crypto_policy.c6
-rw-r--r--fs/ext4/ext4.h27
-rw-r--r--fs/ext4/ext4_crypto.h4
-rw-r--r--fs/ext4/inline.c19
-rw-r--r--fs/ext4/inode.c54
-rw-r--r--fs/ext4/ioctl.c4
-rw-r--r--fs/ext4/readpage.c47
-rw-r--r--fs/ext4/super.c2
-rw-r--r--fs/f2fs/crypto_policy.c5
-rw-r--r--fs/f2fs/data.c41
-rw-r--r--fs/f2fs/file.c9
-rw-r--r--fs/f2fs/inline.c18
-rw-r--r--fs/gfs2/acl.c5
-rw-r--r--fs/gfs2/incore.h2
-rw-r--r--fs/mpage.c36
-rw-r--r--fs/sdcardfs/dentry.c41
-rw-r--r--fs/sdcardfs/derived_perm.c338
-rw-r--r--fs/sdcardfs/file.c98
-rw-r--r--fs/sdcardfs/inode.c110
-rw-r--r--fs/sdcardfs/lookup.c167
-rw-r--r--fs/sdcardfs/main.c95
-rw-r--r--fs/sdcardfs/mmap.c55
-rw-r--r--fs/sdcardfs/multiuser.h23
-rw-r--r--fs/sdcardfs/packagelist.c86
-rw-r--r--fs/sdcardfs/sdcardfs.h135
-rw-r--r--fs/sdcardfs/super.c39
-rw-r--r--fs/squashfs/Kconfig28
-rw-r--r--fs/squashfs/Makefile3
-rw-r--r--fs/squashfs/block.c546
-rw-r--r--fs/squashfs/cache.c73
-rw-r--r--fs/squashfs/decompressor.c55
-rw-r--r--fs/squashfs/file.c140
-rw-r--r--fs/squashfs/file_cache.c38
-rw-r--r--fs/squashfs/file_direct.c245
-rw-r--r--fs/squashfs/lz4_wrapper.c32
-rw-r--r--fs/squashfs/lzo_wrapper.c40
-rw-r--r--fs/squashfs/page_actor.c175
-rw-r--r--fs/squashfs/page_actor.h84
-rw-r--r--fs/squashfs/squashfs.h11
-rw-r--r--fs/squashfs/squashfs_fs_sb.h2
-rw-r--r--fs/squashfs/super.c7
-rw-r--r--fs/squashfs/xz_wrapper.c15
-rw-r--r--fs/squashfs/zlib_wrapper.c14
-rw-r--r--fs/sync.c1
-rw-r--r--fs/xfs/libxfs/xfs_inode_buf.c8
-rw-r--r--fs/xfs/xfs_acl.c4
-rw-r--r--fs/xfs/xfs_bmap_util.c7
-rw-r--r--fs/xfs/xfs_buf.c1
-rw-r--r--include/asm-generic/memory_model.h4
-rw-r--r--include/crypto/algapi.h3
-rw-r--r--include/crypto/gf128mul.h47
-rw-r--r--include/crypto/internal/hash.h8
-rw-r--r--include/drm/drm_crtc.h29
-rw-r--r--include/drm/drm_displayid.h17
-rw-r--r--include/drm/drm_edid.h5
-rw-r--r--include/linux/dcache.h2
-rw-r--r--include/linux/dccp.h1
-rw-r--r--include/linux/device-mapper.h7
-rw-r--r--include/linux/hdmi.h3
-rw-r--r--include/linux/io-pgtable-fast.h3
-rw-r--r--include/linux/iommu.h1
-rw-r--r--include/linux/ipv6.h2
-rw-r--r--include/linux/leds-qpnp-flash.h3
-rw-r--r--include/linux/log2.h13
-rw-r--r--include/linux/mdss_io_util.h4
-rw-r--r--include/linux/mm.h12
-rw-r--r--include/linux/mmc/card.h3
-rw-r--r--include/linux/mmc/mmc.h3
-rw-r--r--include/linux/msm_smd_pkt.h23
-rw-r--r--include/linux/perf/arm_pmu.h8
-rw-r--r--include/linux/pfn.h1
-rw-r--r--include/linux/poison.h4
-rw-r--r--include/linux/qpnp/qpnp-revid.h8
-rw-r--r--include/linux/regulator/qpnp-labibb-regulator.h1
-rw-r--r--include/linux/sched.h8
-rw-r--r--include/linux/shrinker.h1
-rw-r--r--include/linux/task_io_accounting.h2
-rw-r--r--include/linux/task_io_accounting_ops.h1
-rw-r--r--include/linux/timer.h3
-rw-r--r--include/linux/usb.h3
-rw-r--r--include/linux/usb/audio-v3.h172
-rw-r--r--include/linux/usb/ccid_desc.h44
-rw-r--r--include/linux/usb/hcd.h1
-rw-r--r--include/linux/usb/quirks.h6
-rw-r--r--include/media/msm_cam_sensor.h1
-rw-r--r--include/net/addrconf.h3
-rw-r--r--include/net/if_inet6.h1
-rw-r--r--include/scsi/libiscsi.h1
-rw-r--r--include/soc/qcom/minidump.h48
-rw-r--r--include/soc/qcom/socinfo.h6
-rw-r--r--include/soc/qcom/subsystem_restart.h3
-rw-r--r--include/sound/apr_audio-v2.h1
-rw-r--r--include/sound/q6adm-v2.h2
-rw-r--r--include/sound/q6asm-v2.h1
-rw-r--r--include/trace/events/android_fs.h65
-rw-r--r--include/trace/events/android_fs_template.h64
-rw-r--r--include/uapi/drm/drm_mode.h3
-rw-r--r--include/uapi/drm/msm_drm.h11
-rw-r--r--include/uapi/linux/android/binder.h2
-rw-r--r--include/uapi/linux/hbtp_input.h9
-rw-r--r--include/uapi/linux/hw_breakpoint.h4
-rw-r--r--include/uapi/linux/ipv6.h10
-rw-r--r--include/uapi/linux/msm_kgsl.h2
-rw-r--r--include/uapi/linux/netlink.h4
-rw-r--r--include/uapi/linux/netlink_diag.h2
-rw-r--r--include/uapi/linux/packet_diag.h2
-rw-r--r--include/uapi/linux/sysctl.h1
-rw-r--r--include/uapi/linux/usb/audio.h1
-rw-r--r--include/uapi/media/msm_camsensor_sdk.h3
-rw-r--r--include/uapi/media/msmb_ispif.h10
-rw-r--r--include/uapi/sound/compress_params.h3
-rw-r--r--init/do_mounts_dm.c556
-rw-r--r--kernel/cpu.c5
-rw-r--r--kernel/events/core.c5
-rw-r--r--kernel/fork.c15
-rw-r--r--kernel/futex.c22
-rw-r--r--kernel/power/hibernate.c17
-rw-r--r--kernel/sched/core_ctl.c60
-rw-r--r--kernel/sched/deadline.c3
-rw-r--r--kernel/sched/fair.c2
-rw-r--r--kernel/sched/rt.c3
-rw-r--r--kernel/sched/sched.h2
-rw-r--r--kernel/time/tick-sched.c6
-rw-r--r--kernel/time/timer.c34
-rw-r--r--kernel/trace/msm_rtb.c10
-rw-r--r--kernel/watchdog.c2
-rw-r--r--mm/Kconfig.debug53
-rw-r--r--mm/Makefile2
-rw-r--r--mm/page_alloc.c10
-rw-r--r--mm/page_ext.c13
-rw-r--r--mm/page_poison.c (renamed from mm/debug-pagealloc.c)93
-rw-r--r--mm/percpu.c5
-rw-r--r--mm/vmscan.c54
-rw-r--r--net/bridge/br_input.c1
-rw-r--r--net/bridge/br_netfilter_hooks.c21
-rw-r--r--net/ceph/osdmap.c1
-rw-r--r--net/core/dev.c35
-rw-r--r--net/core/skbuff.c30
-rw-r--r--net/core/sock.c16
-rw-r--r--net/dccp/ccids/ccid2.c1
-rw-r--r--net/dccp/ipv4.c3
-rw-r--r--net/dccp/ipv6.c8
-rw-r--r--net/dccp/minisocks.c25
-rw-r--r--net/ipv4/fib_frontend.c3
-rw-r--r--net/ipv4/route.c1
-rw-r--r--net/ipv4/tcp_input.c2
-rw-r--r--net/ipv4/tcp_ipv4.c10
-rw-r--r--net/ipv4/tcp_minisocks.c1
-rw-r--r--net/ipv4/tcp_timer.c6
-rw-r--r--net/ipv6/addrconf.c61
-rw-r--r--net/ipv6/ip6_fib.c2
-rw-r--r--net/ipv6/ip6_output.c7
-rw-r--r--net/ipv6/ip6_vti.c4
-rw-r--r--net/ipv6/ndisc.c2
-rw-r--r--net/ipv6/netfilter/Kconfig12
-rw-r--r--net/ipv6/netfilter/ip6_tables.c30
-rw-r--r--net/ipv6/tcp_ipv6.c8
-rw-r--r--net/l2tp/l2tp_ip.c2
-rw-r--r--net/mpls/af_mpls.c1
-rw-r--r--net/netfilter/xt_qtaguid.c24
-rw-r--r--net/netlink/Kconfig9
-rw-r--r--net/netlink/af_netlink.c751
-rw-r--r--net/netlink/af_netlink.h15
-rw-r--r--net/netlink/diag.c39
-rw-r--r--net/openvswitch/flow_netlink.c2
-rw-r--r--net/packet/af_packet.c8
-rw-r--r--net/sched/act_api.c5
-rw-r--r--net/sched/act_connmark.c3
-rw-r--r--net/unix/garbage.c17
-rw-r--r--net/wireless/db.txt3
-rw-r--r--net/wireless/nl80211.c121
-rw-r--r--net/wireless/scan.c2
-rw-r--r--net/xfrm/xfrm_policy.c10
-rw-r--r--net/xfrm/xfrm_user.c9
-rw-r--r--sound/core/seq/seq_clientmgr.c1
-rw-r--r--sound/core/seq/seq_fifo.c3
-rw-r--r--sound/core/seq/seq_memory.c17
-rw-r--r--sound/core/seq/seq_memory.h1
-rw-r--r--sound/pci/ctxfi/cthw20k1.c2
-rw-r--r--sound/pci/hda/patch_realtek.c2
-rw-r--r--sound/soc/codecs/wcd-dsp-mgr.c31
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c3
-rw-r--r--sound/soc/codecs/wcd-spi.c2
-rw-r--r--sound/soc/codecs/wcd9335.c4
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c14
-rw-r--r--sound/soc/codecs/wcd_cpe_core.c2
-rw-r--r--sound/soc/msm/apq8096-auto.c88
-rw-r--r--sound/soc/msm/msm-dai-fe.c2
-rw-r--r--sound/soc/msm/msm8998.c25
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c25
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-slim.c20
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c69
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c130
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c2
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c3
-rw-r--r--sound/soc/msm/qdsp6v2/q6lsm.c6
-rw-r--r--sound/soc/msm/sdm660-common.c3
-rw-r--r--sound/usb/Makefile3
-rw-r--r--sound/usb/badd.c137
-rw-r--r--sound/usb/card.c39
-rw-r--r--sound/usb/clock.c4
-rw-r--r--sound/usb/format.c49
-rw-r--r--sound/usb/mixer.c496
-rw-r--r--sound/usb/stream.c87
-rw-r--r--sound/usb/usb_audio_qmi_svc.c49
-rw-r--r--tools/include/linux/log2.h13
654 files changed, 21211 insertions, 7268 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 02bf809740c3..781f05879445 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -48,7 +48,7 @@ SoCs:
compatible = "qcom,apqtitanium"
- APQ8998
- compatible = "qcom,apq8998"
+ compatible = "qcom,apq8098"
- MDM9630
compatible = "qcom,mdm9630"
@@ -195,9 +195,9 @@ compatible = "qcom,apq8037-cdp"
compatible = "qcom,apq8037-mtp"
compatible = "qcom,apqtitanium-cdp"
compatible = "qcom,apqtitanium-mtp"
-compatible = "qcom,apq8998-cdp"
-compatible = "qcom,apq8998-mtp"
-compatible = "qcom,apq8998-qrd"
+compatible = "qcom,apq8098-cdp"
+compatible = "qcom,apq8098-mtp"
+compatible = "qcom,apq8098-qrd"
compatible = "qcom,mdm9630-cdp"
compatible = "qcom,mdm9630-mtp"
compatible = "qcom,mdm9630-sim"
@@ -280,6 +280,7 @@ compatible = "qcom,sdm660-rumi"
compatible = "qcom,sdm660-cdp"
compatible = "qcom,sdm660-mtp"
compatible = "qcom,sdm660-qrd"
+compatible = "qcom,sda660-qrd"
compatible = "qcom,sda658-mtp"
compatible = "qcom,sda658-cdp"
compatible = "qcom,sda660-mtp"
@@ -290,6 +291,7 @@ compatible = "qcom,sdm630-cdp"
compatible = "qcom,sda630-mtp"
compatible = "qcom,sda630-cdp"
compatible = "qcom,sdm630-qrd"
+compatible = "qcom,sda630-qrd"
compatible = "qcom,msm8952-rumi"
compatible = "qcom,msm8952-sim"
compatible = "qcom,msm8952-qrd"
diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt
index aa227c2628da..707e6edb26ea 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt
@@ -27,7 +27,46 @@ Required properties
- qcom,aux-en-gpio: Specifies the aux-channel enable gpio.
- qcom,aux-sel-gpio: Specifies the aux-channel select gpio.
- qcom,usbplug-cc-gpio: Specifies the usbplug orientation gpio.
-- qcom,aux-cfg-settings: An array that specifies the DP AUX configuration settings.
+- qcom,aux-cfg0-settings: Specifies the DP AUX configuration 0 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg1-settings: Specifies the DP AUX configuration 1 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg2-settings: Specifies the DP AUX configuration 2 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg3-settings: Specifies the DP AUX configuration 3 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg4-settings: Specifies the DP AUX configuration 4 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg5-settings: Specifies the DP AUX configuration 5 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg6-settings: Specifies the DP AUX configuration 6 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg7-settings: Specifies the DP AUX configuration 7 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg8-settings: Specifies the DP AUX configuration 8 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg9-settings: Specifies the DP AUX configuration 9 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
Optional properties:
- qcom,<type>-supply-entries: A node that lists the elements of the supply used by the
@@ -87,7 +126,16 @@ Example:
"core_aux_clk", "core_cfg_ahb_clk", "ctrl_link_clk",
"ctrl_link_iface_clk", "ctrl_crypto_clk", "ctrl_pixel_clk";
- qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03];
+ qcom,aux-cfg0-settings = [1c 00];
+ qcom,aux-cfg1-settings = [20 13 23 1d];
+ qcom,aux-cfg2-settings = [24 00];
+ qcom,aux-cfg3-settings = [28 00];
+ qcom,aux-cfg4-settings = [2c 0a];
+ qcom,aux-cfg5-settings = [30 26];
+ qcom,aux-cfg6-settings = [34 0a];
+ qcom,aux-cfg7-settings = [38 03];
+ qcom,aux-cfg8-settings = [3c bb];
+ qcom,aux-cfg9-settings = [40 03];
qcom,logical2physical-lane-map = [02 03 01 00];
qcom,phy-register-offset = <0x4>;
qcom,max-pclk-frequency-khz = <593470>;
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 90ccfa7c62e2..ce5ee56ada68 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -581,6 +581,15 @@ Additional properties added to the second level nodes that represent timings pro
commands.
"dsi_lp_mode" = DSI low power mode (default)
"dsi_hs_mode" = DSI high speed mode
+- qcom,sublinks-count: An integer value indicates the number of sublinks in the panel.
+ Default value is 1. This property is used only if qcom,split-link-enabled
+ is defined.
+- qcom,lanes-per-sublink: An integer value indicates the number of data lanes per sublink in the panel.
+ Default value is 1. This property is used only if qcom,split-link-enabled
+ is defined.
+- qcom,split-link-enabled: A boolean value to enable/disable the split link feature. If qcom,sublinks-count
+ or qcom,lanes-per-sublink are not defined, default values are used.
+
Note, if a given optional qcom,* binding is not present, then the driver will configure
the default values specified.
@@ -808,6 +817,10 @@ Example:
qcom,mdss-dsc-version = <0x11>;
qcom,mdss-dsc-scr-version = <0x1>;
+ qcom,split-link-enabled;
+ qcom,sublinks-count = <2>;
+ qcom,lanes-per-sublink = <2>;
+
dsi_sim_vid_config0: config0 {
qcom,lm-split = <360 360>;
qcom,mdss-dsc-encoders = <2>;
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi.txt b/Documentation/devicetree/bindings/fb/mdss-dsi.txt
index 6c20d22f98b4..0d55389f3790 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi.txt
@@ -50,6 +50,8 @@ Optional properties:
-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
+ -- qcom,supply-lp-mode-disable-allowed: supply can be turned off in
+ low power state.
- pll-src-config Specified the source PLL for the DSI
link clocks:
"PLL0" - Clocks sourced out of DSI PLL0
diff --git a/Documentation/devicetree/bindings/input/qpnp-power-on.txt b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
index a596aa1c595d..c2550e6fe26c 100644
--- a/Documentation/devicetree/bindings/input/qpnp-power-on.txt
+++ b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
@@ -82,6 +82,8 @@ Optional properties:
- qcom,shutdown-poweroff-type Same description as qcom,warm-reset-poweroff-
type but this applies for the system shutdown
case.
+- qcom,kpdpwr-sw-debounce Boolean property to enable the debounce logic
+ on the KPDPWR_N rising edge.
All the below properties are in the sub-node section (properties of the child
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
index addb0a6869ac..12d32ec74369 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
@@ -96,6 +96,14 @@ First Level Node - FG Gen3 device
This value has to be specified in negative values for
the charging current.
+- qcom,fg-chg-term-base-current
+ Usage: optional
+ Value type: <u32>
+ Definition: Battery current (in mA) upper boundary at which the fuel
+ gauge will issue an end of charge during discharging. If
+ this property is not specified, then the default value used
+ will be 75mA.
+
- qcom,fg-delta-soc-thr
Usage: optional
Value type: <u32>
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index 5a2c3ecd3d1e..894c34553a22 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -169,6 +169,18 @@ Charger specific properties:
Definition: Boolean flag which when present enables intput suspend for
debug battery.
+- qcom,min-freq-khz
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies the minimum charger buck/boost switching frequency
+ in KHz. It overrides the min frequency defined for the charger.
+
+- qcom,max-freq-khz
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies the maximum charger buck/boost switching frequency in
+ KHz. It overrides the max frequency defined for the charger.
+
=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
index 92ef23c3a290..5529e30811fb 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
@@ -22,7 +22,8 @@ Charger specific properties:
Definition: String which indicates the charging mode. Can be one of the
following:
Standalone/Parallel Master - "qcom,smb138x-charger"
- Parallel Slave - "qcom,smb138x-parallel-slave"
+ smb138x Parallel Slave - "qcom,smb138x-parallel-slave"
+ smb1355 Parallel Slave - "qcom,smb1355-parallel-slave",
- qcom,pmic-revid
Usage: required
@@ -35,7 +36,8 @@ Charger specific properties:
Usage: optional
Value type: <u32>
Definition: Specifies parallel charging mode. If not specified, MID-MID
- option is selected by default.
+ option is selected by default. Note that smb1355 can only
+ run in MID-MID configuration.
- qcom,suspend-input
Usage: optional
@@ -125,7 +127,7 @@ Example
=======
smb138x_charger: qcom,smb138x-charger {
- compatible = "qcom,qpnp-smb138x-charger";
+ compatible = "qcom,smb138x-charger";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
index c9cfc889faba..0d53b9fa4378 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
@@ -151,6 +151,10 @@ LAB subnode optional properties:
any value in the allowed limit.
- qcom,notify-lab-vreg-ok-sts: A boolean property which upon set will
poll and notify the lab_vreg_ok status.
+- qcom,qpnp-lab-sc-wait-time-ms: This property is used to specify the time
+ (in ms) to poll for the short circuit
+ detection. If not specified the default time
+ is 5 sec.
Following properties are available only for PM660A:
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
index 38f599ba5321..55fde0d4feb6 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
@@ -14,6 +14,11 @@ Required Node Structure
Value type: <string>
Definition: should be "qcom,qpnp-oledb-regulator".
+- qcom,pmic-revid
+ Usage: required
+ Value type: <phandle>
+ Definition: Used to identify the PMIC subtype.
+
- reg
Usage: required
Value type: <prop-encoded-array>
@@ -57,13 +62,6 @@ Required Node Structure
rail. This property is applicable only if qcom,ext-pin-ctl
property is specified and it is specific to PM660A.
-- qcom,force-pd-control
- Usage: optional
- Value type: <bool>
- Definition: Used to enable the pull down control forcibly via SPMI by
- disabling the pull down configuration done by hardware
- automatically through SWIRE pulses.
-
- qcom,pbs-client
Usage: optional
Value type: <phandle>
@@ -224,6 +222,7 @@ pm660a_oledb: qpnp-oledb@e000 {
compatible = "qcom,qpnp-oledb-regulator";
#address-cells = <1>;
#size-cells = <1>;
+ qcom,pmic-revid = <&pm660l_revid>;
reg = <0xe000 0x100>;
label = "oledb";
diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
index bef919334574..633abd2b8d08 100644
--- a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
+++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
@@ -42,6 +42,9 @@ Required properties:
cell 4: interrupt flags indicating level-sense information, as defined in
dt-bindings/interrupt-controller/irq.h
+Optional properties:
+- qcom,reserved-chan : Reserved channel for debug purpose
+
Example V1 PMIC-Arbiter:
spmi {
@@ -56,6 +59,7 @@ Example V1 PMIC-Arbiter:
qcom,ee = <0>;
qcom,channel = <0>;
+ qcom,reserved-chan = <511>;
#address-cells = <2>;
#size-cells = <0>;
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 0220f18658e8..e953469cbe5e 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2692,6 +2692,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
we can turn it on.
on: enable the feature
+ page_poison= [KNL] Boot-time parameter changing the state of
+ poisoning on the buddy allocator.
+ off: turn off poisoning
+ on: turn on poisoning
+
panic= [KNL] Kernel behaviour on panic: delay <timeout>
timeout > 0: seconds before rebooting
timeout = 0: wait forever
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 2d8fe6499090..f6851d94c1af 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1418,11 +1418,20 @@ accept_ra_pinfo - BOOLEAN
Functional default: enabled if accept_ra is enabled.
disabled if accept_ra is disabled.
+accept_ra_rt_info_min_plen - INTEGER
+ Minimum prefix length of Route Information in RA.
+
+ Route Information w/ prefix smaller than this variable shall
+ be ignored.
+
+ Functional default: 0 if accept_ra_rtr_pref is enabled.
+ -1 if accept_ra_rtr_pref is disabled.
+
accept_ra_rt_info_max_plen - INTEGER
Maximum prefix length of Route Information in RA.
- Route Information w/ prefix larger than or equal to this
- variable shall be ignored.
+ Route Information w/ prefix larger than this variable shall
+ be ignored.
Functional default: 0 if accept_ra_rtr_pref is enabled.
-1 if accept_ra_rtr_pref is disabled.
diff --git a/Documentation/networking/netlink_mmap.txt b/Documentation/networking/netlink_mmap.txt
deleted file mode 100644
index 54f10478e8e3..000000000000
--- a/Documentation/networking/netlink_mmap.txt
+++ /dev/null
@@ -1,332 +0,0 @@
-This file documents how to use memory mapped I/O with netlink.
-
-Author: Patrick McHardy <kaber@trash.net>
-
-Overview
---------
-
-Memory mapped netlink I/O can be used to increase throughput and decrease
-overhead of unicast receive and transmit operations. Some netlink subsystems
-require high throughput, these are mainly the netfilter subsystems
-nfnetlink_queue and nfnetlink_log, but it can also help speed up large
-dump operations of f.i. the routing database.
-
-Memory mapped netlink I/O used two circular ring buffers for RX and TX which
-are mapped into the processes address space.
-
-The RX ring is used by the kernel to directly construct netlink messages into
-user-space memory without copying them as done with regular socket I/O,
-additionally as long as the ring contains messages no recvmsg() or poll()
-syscalls have to be issued by user-space to get more message.
-
-The TX ring is used to process messages directly from user-space memory, the
-kernel processes all messages contained in the ring using a single sendmsg()
-call.
-
-Usage overview
---------------
-
-In order to use memory mapped netlink I/O, user-space needs three main changes:
-
-- ring setup
-- conversion of the RX path to get messages from the ring instead of recvmsg()
-- conversion of the TX path to construct messages into the ring
-
-Ring setup is done using setsockopt() to provide the ring parameters to the
-kernel, then a call to mmap() to map the ring into the processes address space:
-
-- setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, &params, sizeof(params));
-- setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, &params, sizeof(params));
-- ring = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
-
-Usage of either ring is optional, but even if only the RX ring is used the
-mapping still needs to be writable in order to update the frame status after
-processing.
-
-Conversion of the reception path involves calling poll() on the file
-descriptor, once the socket is readable the frames from the ring are
-processed in order until no more messages are available, as indicated by
-a status word in the frame header.
-
-On kernel side, in order to make use of memory mapped I/O on receive, the
-originating netlink subsystem needs to support memory mapped I/O, otherwise
-it will use an allocated socket buffer as usual and the contents will be
- copied to the ring on transmission, nullifying most of the performance gains.
-Dumps of kernel databases automatically support memory mapped I/O.
-
-Conversion of the transmit path involves changing message construction to
-use memory from the TX ring instead of (usually) a buffer declared on the
-stack and setting up the frame header appropriately. Optionally poll() can
-be used to wait for free frames in the TX ring.
-
-Structured and definitions for using memory mapped I/O are contained in
-<linux/netlink.h>.
-
-RX and TX rings
-----------------
-
-Each ring contains a number of continuous memory blocks, containing frames of
-fixed size dependent on the parameters used for ring setup.
-
-Ring: [ block 0 ]
- [ frame 0 ]
- [ frame 1 ]
- [ block 1 ]
- [ frame 2 ]
- [ frame 3 ]
- ...
- [ block n ]
- [ frame 2 * n ]
- [ frame 2 * n + 1 ]
-
-The blocks are only visible to the kernel, from the point of view of user-space
-the ring just contains the frames in a continuous memory zone.
-
-The ring parameters used for setting up the ring are defined as follows:
-
-struct nl_mmap_req {
- unsigned int nm_block_size;
- unsigned int nm_block_nr;
- unsigned int nm_frame_size;
- unsigned int nm_frame_nr;
-};
-
-Frames are grouped into blocks, where each block is a continuous region of memory
-and holds nm_block_size / nm_frame_size frames. The total number of frames in
-the ring is nm_frame_nr. The following invariants hold:
-
-- frames_per_block = nm_block_size / nm_frame_size
-
-- nm_frame_nr = frames_per_block * nm_block_nr
-
-Some parameters are constrained, specifically:
-
-- nm_block_size must be a multiple of the architectures memory page size.
- The getpagesize() function can be used to get the page size.
-
-- nm_frame_size must be equal or larger to NL_MMAP_HDRLEN, IOW a frame must be
- able to hold at least the frame header
-
-- nm_frame_size must be smaller or equal to nm_block_size
-
-- nm_frame_size must be a multiple of NL_MMAP_MSG_ALIGNMENT
-
-- nm_frame_nr must equal the actual number of frames as specified above.
-
-When the kernel can't allocate physically continuous memory for a ring block,
-it will fall back to use physically discontinuous memory. This might affect
-performance negatively, in order to avoid this the nm_frame_size parameter
-should be chosen to be as small as possible for the required frame size and
-the number of blocks should be increased instead.
-
-Ring frames
-------------
-
-Each frames contain a frame header, consisting of a synchronization word and some
-meta-data, and the message itself.
-
-Frame: [ header message ]
-
-The frame header is defined as follows:
-
-struct nl_mmap_hdr {
- unsigned int nm_status;
- unsigned int nm_len;
- __u32 nm_group;
- /* credentials */
- __u32 nm_pid;
- __u32 nm_uid;
- __u32 nm_gid;
-};
-
-- nm_status is used for synchronizing processing between the kernel and user-
- space and specifies ownership of the frame as well as the operation to perform
-
-- nm_len contains the length of the message contained in the data area
-
-- nm_group specified the destination multicast group of message
-
-- nm_pid, nm_uid and nm_gid contain the netlink pid, UID and GID of the sending
- process. These values correspond to the data available using SOCK_PASSCRED in
- the SCM_CREDENTIALS cmsg.
-
-The possible values in the status word are:
-
-- NL_MMAP_STATUS_UNUSED:
- RX ring: frame belongs to the kernel and contains no message
- for user-space. Approriate action is to invoke poll()
- to wait for new messages.
-
- TX ring: frame belongs to user-space and can be used for
- message construction.
-
-- NL_MMAP_STATUS_RESERVED:
- RX ring only: frame is currently used by the kernel for message
- construction and contains no valid message yet.
- Appropriate action is to invoke poll() to wait for
- new messages.
-
-- NL_MMAP_STATUS_VALID:
- RX ring: frame contains a valid message. Approriate action is
- to process the message and release the frame back to
- the kernel by setting the status to
- NL_MMAP_STATUS_UNUSED or queue the frame by setting the
- status to NL_MMAP_STATUS_SKIP.
-
- TX ring: the frame contains a valid message from user-space to
- be processed by the kernel. After completing processing
- the kernel will release the frame back to user-space by
- setting the status to NL_MMAP_STATUS_UNUSED.
-
-- NL_MMAP_STATUS_COPY:
- RX ring only: a message is ready to be processed but could not be
- stored in the ring, either because it exceeded the
- frame size or because the originating subsystem does
- not support memory mapped I/O. Appropriate action is
- to invoke recvmsg() to receive the message and release
- the frame back to the kernel by setting the status to
- NL_MMAP_STATUS_UNUSED.
-
-- NL_MMAP_STATUS_SKIP:
- RX ring only: user-space queued the message for later processing, but
- processed some messages following it in the ring. The
- kernel should skip this frame when looking for unused
- frames.
-
-The data area of a frame begins at a offset of NL_MMAP_HDRLEN relative to the
-frame header.
-
-TX limitations
---------------
-
-As of Jan 2015 the message is always copied from the ring frame to an
-allocated buffer due to unresolved security concerns.
-See commit 4682a0358639b29cf ("netlink: Always copy on mmap TX.").
-
-Example
--------
-
-Ring setup:
-
- unsigned int block_size = 16 * getpagesize();
- struct nl_mmap_req req = {
- .nm_block_size = block_size,
- .nm_block_nr = 64,
- .nm_frame_size = 16384,
- .nm_frame_nr = 64 * block_size / 16384,
- };
- unsigned int ring_size;
- void *rx_ring, *tx_ring;
-
- /* Configure ring parameters */
- if (setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, &req, sizeof(req)) < 0)
- exit(1);
- if (setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, &req, sizeof(req)) < 0)
- exit(1)
-
- /* Calculate size of each individual ring */
- ring_size = req.nm_block_nr * req.nm_block_size;
-
- /* Map RX/TX rings. The TX ring is located after the RX ring */
- rx_ring = mmap(NULL, 2 * ring_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
- if ((long)rx_ring == -1L)
- exit(1);
- tx_ring = rx_ring + ring_size:
-
-Message reception:
-
-This example assumes some ring parameters of the ring setup are available.
-
- unsigned int frame_offset = 0;
- struct nl_mmap_hdr *hdr;
- struct nlmsghdr *nlh;
- unsigned char buf[16384];
- ssize_t len;
-
- while (1) {
- struct pollfd pfds[1];
-
- pfds[0].fd = fd;
- pfds[0].events = POLLIN | POLLERR;
- pfds[0].revents = 0;
-
- if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
- exit(1);
-
- /* Check for errors. Error handling omitted */
- if (pfds[0].revents & POLLERR)
- <handle error>
-
- /* If no new messages, poll again */
- if (!(pfds[0].revents & POLLIN))
- continue;
-
- /* Process all frames */
- while (1) {
- /* Get next frame header */
- hdr = rx_ring + frame_offset;
-
- if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
- /* Regular memory mapped frame */
- nlh = (void *)hdr + NL_MMAP_HDRLEN;
- len = hdr->nm_len;
-
- /* Release empty message immediately. May happen
- * on error during message construction.
- */
- if (len == 0)
- goto release;
- } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
- /* Frame queued to socket receive queue */
- len = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
- if (len <= 0)
- break;
- nlh = buf;
- } else
- /* No more messages to process, continue polling */
- break;
-
- process_msg(nlh);
-release:
- /* Release frame back to the kernel */
- hdr->nm_status = NL_MMAP_STATUS_UNUSED;
-
- /* Advance frame offset to next frame */
- frame_offset = (frame_offset + frame_size) % ring_size;
- }
- }
-
-Message transmission:
-
-This example assumes some ring parameters of the ring setup are available.
-A single message is constructed and transmitted, to send multiple messages
-at once they would be constructed in consecutive frames before a final call
-to sendto().
-
- unsigned int frame_offset = 0;
- struct nl_mmap_hdr *hdr;
- struct nlmsghdr *nlh;
- struct sockaddr_nl addr = {
- .nl_family = AF_NETLINK,
- };
-
- hdr = tx_ring + frame_offset;
- if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
- /* No frame available. Use poll() to avoid. */
- exit(1);
-
- nlh = (void *)hdr + NL_MMAP_HDRLEN;
-
- /* Build message */
- build_message(nlh);
-
- /* Fill frame header: length and status need to be set */
- hdr->nm_len = nlh->nlmsg_len;
- hdr->nm_status = NL_MMAP_STATUS_VALID;
-
- if (sendto(fd, NULL, 0, 0, &addr, sizeof(addr)) < 0)
- exit(1);
-
- /* Advance frame offset to next frame */
- frame_offset = (frame_offset + frame_size) % ring_size;
diff --git a/Makefile b/Makefile
index 3a8de39e5823..759c1646965e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 4
-SUBLEVEL = 55
+SUBLEVEL = 59
EXTRAVERSION =
NAME = Blurry Fish Butt
@@ -146,7 +146,7 @@ PHONY += $(MAKECMDGOALS) sub-make
$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
@:
-sub-make: FORCE
+sub-make:
$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \
-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))
@@ -1005,7 +1005,7 @@ prepare1: prepare2 $(version_h) include/generated/utsrelease.h \
archprepare: archheaders archscripts prepare1 scripts_basic
-prepare0: archprepare FORCE
+prepare0: archprepare
$(Q)$(MAKE) $(build)=.
# All the preparing..
@@ -1050,7 +1050,7 @@ INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware
export INSTALL_FW_PATH
PHONY += firmware_install
-firmware_install: FORCE
+firmware_install:
@mkdir -p $(objtree)/firmware
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_install
@@ -1070,7 +1070,7 @@ PHONY += archscripts
archscripts:
PHONY += __headers
-__headers: $(version_h) scripts_basic asm-generic archheaders archscripts FORCE
+__headers: $(version_h) scripts_basic asm-generic archheaders archscripts
$(Q)$(MAKE) $(build)=scripts build_unifdef
PHONY += headers_install_all
diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg
index f10371a981b7..67842e7007a8 100644
--- a/android/configs/android-base.cfg
+++ b/android/configs/android-base.cfg
@@ -1,17 +1,19 @@
# KEEP ALPHABETICALLY SORTED
# CONFIG_DEVKMEM is not set
# CONFIG_DEVMEM is not set
+# CONFIG_FHANDLE is not set
# CONFIG_INET_LRO is not set
# CONFIG_MODULES is not set
# CONFIG_OABI_COMPAT is not set
# CONFIG_SYSVIPC is not set
+# CONFIG_USELIB is not set
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_ASHMEM=y
CONFIG_AUDIT=y
-CONFIG_BLK_DEV_DM=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_CGROUPS=y
CONFIG_CGROUP_CPUACCT=y
@@ -19,14 +21,15 @@ CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_SCHED=y
CONFIG_CP15_BARRIER_EMULATION=y
-CONFIG_DM_CRYPT=y
-CONFIG_DM_VERITY=y
-CONFIG_DM_VERITY_FEC=y
+CONFIG_DEFAULT_SECURITY_SELINUX=y
CONFIG_EMBEDDED=y
CONFIG_FB=y
CONFIG_HARDENED_USERCOPY=y
CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
CONFIG_INET6_AH=y
+CONFIG_INET6_DIAG_DESTROY=y
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_INET=y
@@ -42,7 +45,6 @@ CONFIG_IPV6=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
-CONFIG_IPV6_PRIVACY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
CONFIG_IP_ADVANCED_ROUTER=y
@@ -64,6 +66,9 @@ CONFIG_IP_NF_TARGET_MASQUERADE=y
CONFIG_IP_NF_TARGET_NETMAP=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
CONFIG_NET=y
CONFIG_NETDEVICES=y
CONFIG_NETFILTER=y
@@ -157,16 +162,16 @@ CONFIG_STAGING=y
CONFIG_SWP_EMULATION=y
CONFIG_SYNC=y
CONFIG_TUN=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_UNIX=y
-CONFIG_USB_GADGET=y
CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_ACC=y
+CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_MTP=y
CONFIG_USB_CONFIGFS_F_PTP=y
-CONFIG_USB_CONFIGFS_F_ACC=y
-CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
CONFIG_USB_CONFIGFS_UEVENT=y
-CONFIG_USB_CONFIGFS_F_MIDI=y
+CONFIG_USB_GADGET=y
CONFIG_USB_OTG_WAKELOCK=y
CONFIG_XFRM_USER=y
diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg
index 70aaae17ad29..11df0892301b 100644
--- a/android/configs/android-recommended.cfg
+++ b/android/configs/android-recommended.cfg
@@ -7,16 +7,22 @@
# CONFIG_PM_WAKELOCKS_GC is not set
# CONFIG_VT is not set
CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ARM64_SW_TTBR0_PAN=y
CONFIG_ARM_KERNMEM_PERMS=y
CONFIG_ARM64_SW_TTBR0_PAN=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BLK_DEV_DM=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_COMPACTION=y
+CONFIG_CPU_SW_DOMAIN_PAN=y
CONFIG_DEBUG_RODATA=y
+CONFIG_DM_CRYPT=y
CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
CONFIG_DRAGONRISE_FF=y
CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_EXT4_FS=y
@@ -92,6 +98,7 @@ CONFIG_LOGIRUMBLEPAD2_FF=y
CONFIG_LOGITECH_FF=y
CONFIG_MD=y
CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEMORY_STATE_TIME=y
CONFIG_MSDOS_FS=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_PANTHERLORD_FF=y
@@ -121,7 +128,6 @@ CONFIG_TIMER_STATS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_UHID=y
-CONFIG_MEMORY_STATE_TIME=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_HIDDEV=y
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1886a65eee87..588393412271 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -113,7 +113,7 @@ if ARM_DMA_USE_IOMMU
config ARM_DMA_IOMMU_ALIGNMENT
int "Maximum PAGE_SIZE order of alignment for DMA IOMMU buffers"
range 4 9
- default 8
+ default 9
help
DMA mapping framework by default aligns all buffers to the smallest
PAGE_SIZE order which is greater than or equal to the requested buffer
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 74aefe4e616d..27f79c288c61 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -102,6 +102,28 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \
apq8096-v3-pmi8996-mdm9x55-slimbus-mtp.dtb \
apq8096-v3-pmi8996-dragonboard.dtb
+ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y)
+dtbo-$(CONFIG_ARCH_MSM8998) += \
+ msm8998-cdp-overlay.dtbo \
+ msm8998-mtp-overlay.dtbo \
+ msm8998-v2-cdp-overlay.dtbo \
+ msm8998-v2-mtp-overlay.dtbo \
+ msm8998-v2.1-cdp-overlay.dtbo \
+ msm8998-v2.1-mtp-overlay.dtbo \
+ msm8998-qrd-overlay.dtbo \
+ msm8998-qrd-vr1-overlay.dtbo \
+ msm8998-qrd-skuk-overlay.dtbo
+
+msm8998-cdp-overlay.dtbo-base := msm8998.dtb
+msm8998-mtp-overlay.dtbo-base := msm8998.dtb
+msm8998-v2-cdp-overlay.dtbo-base := msm8998-v2.dtb
+msm8998-v2-mtp-overlay.dtbo-base := msm8998-v2.dtb
+msm8998-v2.1-cdp-overlay.dtbo-base := msm8998-v2.1.dtb
+msm8998-v2.1-mtp-overlay.dtbo-base := msm8998-v2.1.dtb
+msm8998-qrd-overlay.dtbo-base := msm8998-qrd.dtb
+msm8998-qrd-vr1-overlay.dtbo-base := msm8998-qrd-vr1.dtb
+msm8998-qrd-skuk-overlay.dtbo-base := msm8998-qrd-skuk.dtb
+else
dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \
msm8998-rumi.dtb \
msm8998-cdp.dtb \
@@ -118,22 +140,24 @@ dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \
msm8998-v2-qrd-vr1.dtb \
msm8998-v2-qrd-skuk-evt3.dtb \
msm8998-v2-qrd-skuk-hdk.dtb \
- apq8998-mtp.dtb \
- apq8998-cdp.dtb \
- apq8998-v2-mtp.dtb \
- apq8998-v2-cdp.dtb \
- apq8998-v2-qrd.dtb \
- apq8998-v2-qrd-skuk-hdk.dtb \
+ apq8098-mtp.dtb \
+ apq8098-cdp.dtb \
+ apq8098-v2-mtp.dtb \
+ apq8098-v2-cdp.dtb \
+ apq8098-v2-qrd.dtb \
+ apq8098-v2-qrd-skuk-hdk.dtb \
msm8998-v2.1-mtp.dtb \
+ msm8998-v2.1-mtp-4k-display.dtb \
msm8998-v2.1-cdp.dtb \
msm8998-v2.1-qrd.dtb \
- apq8998-v2.1-mtp.dtb \
- apq8998-v2.1-cdp.dtb \
- apq8998-v2.1-qrd.dtb \
- apq8998-v2.1-mediabox.dtb \
+ apq8098-v2.1-mtp.dtb \
+ apq8098-v2.1-cdp.dtb \
+ apq8098-v2.1-qrd.dtb \
+ apq8098-v2.1-mediabox.dtb \
msm8998-v2.1-interposer-sdm660-cdp.dtb \
msm8998-v2.1-interposer-sdm660-mtp.dtb \
msm8998-v2.1-interposer-sdm660-qrd.dtb
+endif
dtb-$(CONFIG_ARCH_MSMHAMSTER) += msmhamster-rumi.dtb
@@ -161,6 +185,7 @@ dtb-$(CONFIG_ARCH_SDM660) += sdm660-sim.dtb \
sda660-pm660a-cdp.dtb \
sda660-pm660a-mtp.dtb \
sda660-pm660a-rcm.dtb \
+ sda660-pm660a-qrd-hdk.dtb \
sdm660-headset-jacktype-no-cdp.dtb \
sdm660-headset-jacktype-no-rcm.dtb \
sdm660-pm660a-headset-jacktype-no-cdp.dtb \
@@ -211,6 +236,7 @@ dtb-$(CONFIG_ARCH_SDM630) += sdm630-rumi.dtb \
sda630-pm660a-mtp.dtb \
sda630-pm660a-cdp.dtb \
sda630-pm660a-rcm.dtb \
+ sda630-pm660a-qrd-hdk.dtb \
sdm630-headset-jacktype-no-cdp.dtb \
sdm630-headset-jacktype-no-rcm.dtb \
sdm630-pm660a-headset-jacktype-no-cdp.dtb \
@@ -218,6 +244,7 @@ dtb-$(CONFIG_ARCH_SDM630) += sdm630-rumi.dtb \
ifeq ($(CONFIG_ARM64),y)
always := $(dtb-y)
+always += $(dtbo-y)
subdir-y := $(dts-dirs)
else
targets += dtbs
@@ -228,4 +255,4 @@ $(obj)/../%.dtb: $(src)/%.dts FORCE
dtbs: $(addprefix $(obj)/../,$(dtb-y))
endif
-clean-files := *.dtb
+clean-files := *.dtbo *.dtb
diff --git a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
index e74aded8c9e3..57a0467c6fc2 100644
--- a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
@@ -633,12 +633,13 @@
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
- <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
- <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>,
- <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>,
- <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>,
- <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
- <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
+ <&dai_tert_tdm_rx_4>, <&dai_tert_tdm_tx_0>,
+ <&dai_tert_tdm_tx_1>, <&dai_tert_tdm_tx_2>,
+ <&dai_tert_tdm_tx_3>, <&dai_quat_tdm_rx_0>,
+ <&dai_quat_tdm_rx_1>, <&dai_quat_tdm_rx_2>,
+ <&dai_quat_tdm_rx_3>, <&dai_quat_tdm_tx_0>,
+ <&dai_quat_tdm_tx_1>, <&dai_quat_tdm_tx_2>,
+ <&dai_quat_tdm_tx_3>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
"msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
@@ -650,12 +651,13 @@
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
- "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
- "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903",
- "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914",
- "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918",
- "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915",
- "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919";
+ "msm-dai-q6-tdm.36904", "msm-dai-q6-tdm.36897",
+ "msm-dai-q6-tdm.36899", "msm-dai-q6-tdm.36901",
+ "msm-dai-q6-tdm.36903", "msm-dai-q6-tdm.36912",
+ "msm-dai-q6-tdm.36914", "msm-dai-q6-tdm.36916",
+ "msm-dai-q6-tdm.36918", "msm-dai-q6-tdm.36913",
+ "msm-dai-q6-tdm.36915", "msm-dai-q6-tdm.36917",
+ "msm-dai-q6-tdm.36919";
asoc-codec = <&stub_codec>;
asoc-codec-names = "msm-stub-codec.1";
};
@@ -689,6 +691,25 @@
};
};
+ qcom,msm-dai-tdm-tert-rx {
+ qcom,msm-cpudai-tdm-group-num-ports = <5>;
+ qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900
+ 36902 36904>;
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&tert_tdm_dout_active>;
+ pinctrl-1 = <&tert_tdm_dout_sleep>;
+ dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36904>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
qcom,msm-dai-tdm-quat-rx {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&quat_tdm_dout_active>;
diff --git a/arch/arm/boot/dts/qcom/apq8998-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-cdp.dts
index 8acd2dabe18a..c8011ff957c0 100644
--- a/arch/arm/boot/dts/qcom/apq8998-cdp.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-cdp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,11 +12,11 @@
/dts-v1/;
-#include "apq8998.dtsi"
+#include "apq8098.dtsi"
#include "msm8998-cdp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 CDP";
- compatible = "qcom,apq8998-cdp", "qcom,apq8998", "qcom,cdp";
+ model = "Qualcomm Technologies, Inc. APQ 8098 CDP";
+ compatible = "qcom,apq8098-cdp", "qcom,apq8098", "qcom,cdp";
qcom,board-id = <1 0>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-mtp.dts
index 5bed816f77d6..7af82a91e935 100644
--- a/arch/arm/boot/dts/qcom/apq8998-mtp.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-mtp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,11 +12,11 @@
/dts-v1/;
-#include "apq8998.dtsi"
+#include "apq8098.dtsi"
#include "msm8998-mtp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 MTP";
- compatible = "qcom,apq8998-mtp", "qcom,apq8998", "qcom,mtp";
+ model = "Qualcomm Technologies, Inc. APQ 8098 MTP";
+ compatible = "qcom,apq8098-mtp", "qcom,apq8098", "qcom,mtp";
qcom,board-id = <8 0>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts
index 94c6031854fa..b01c85bdbac6 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2.1-cdp.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,11 +12,11 @@
/dts-v1/;
-#include "apq8998-v2.1.dtsi"
+#include "apq8098-v2.dtsi"
#include "msm8998-cdp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2.1 CDP";
- compatible = "qcom,apq8998-cdp", "qcom,apq8998", "qcom,cdp";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2 CDP";
+ compatible = "qcom,apq8098-cdp", "qcom,apq8098", "qcom,cdp";
qcom,board-id = <1 0>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts
index 4dc735c8d182..a6e6f15bfa34 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2-mtp.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,11 +12,11 @@
/dts-v1/;
-#include "apq8998-v2.dtsi"
+#include "apq8098-v2.dtsi"
#include "msm8998-mtp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2 MTP";
- compatible = "qcom,apq8998-mtp", "qcom,apq8998", "qcom,mtp";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2 MTP";
+ compatible = "qcom,apq8098-mtp", "qcom,apq8098", "qcom,mtp";
qcom,board-id = <8 0>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts b/arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts
index 6406fe52242d..7aeb8e36cca2 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts
@@ -13,11 +13,11 @@
/dts-v1/;
-#include "apq8998-v2.dtsi"
+#include "apq8098-v2.dtsi"
#include "msm8998-qrd-skuk-hdk.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 SKUK HDK";
+ model = "Qualcomm Technologies, Inc. APQ 8098 SKUK HDK";
compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
qcom,board-id = <0x06000b 0x10>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-qrd.dts b/arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts
index 4f7efa7b4357..0b808e47c915 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2-qrd.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,11 +12,11 @@
/dts-v1/;
-#include "apq8998-v2.dtsi"
+#include "apq8098-v2.dtsi"
#include "msm8998-qrd.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2 QRD";
- compatible = "qcom,apq8998-qrd", "qcom,apq8998", "qcom,qrd";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2 QRD";
+ compatible = "qcom,apq8098-qrd", "qcom,apq8098", "qcom,qrd";
qcom,board-id = <11 0>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts
index 397892dbb540..e41a66eff103 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2-cdp.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,11 +12,11 @@
/dts-v1/;
-#include "apq8998-v2.dtsi"
+#include "apq8098-v2.1.dtsi"
#include "msm8998-cdp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2 CDP";
- compatible = "qcom,apq8998-cdp", "qcom,apq8998", "qcom,cdp";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 CDP";
+ compatible = "qcom,apq8098-cdp", "qcom,apq8098", "qcom,cdp";
qcom,board-id = <1 0>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts
index 9d4b1457f990..d9b1cb8741eb 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts
@@ -12,12 +12,12 @@
/dts-v1/;
-#include "apq8998-v2.1.dtsi"
+#include "apq8098-v2.1.dtsi"
#include "msm8998-cdp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2.1 mediabox";
- compatible = "qcom,apq8998-cdp", "qcom,apq8998", "qcom,cdp";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 mediabox";
+ compatible = "qcom,apq8098-cdp", "qcom,apq8098", "qcom,cdp";
qcom,board-id = <8 1>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts
index fa4e28e515b5..5684e1849b0d 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2.1-mtp.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,11 +12,11 @@
/dts-v1/;
-#include "apq8998-v2.1.dtsi"
+#include "apq8098-v2.1.dtsi"
#include "msm8998-mtp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2.1 MTP";
- compatible = "qcom,apq8998-mtp", "qcom,apq8998", "qcom,mtp";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 MTP";
+ compatible = "qcom,apq8098-mtp", "qcom,apq8098", "qcom,mtp";
qcom,board-id = <8 0>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-qrd.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts
index 6a91b966a9b7..0f315d7c893d 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2.1-qrd.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,11 +12,11 @@
/dts-v1/;
-#include "apq8998-v2.1.dtsi"
+#include "apq8098-v2.1.dtsi"
#include "msm8998-qrd.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2.1 QRD";
- compatible = "qcom,apq8998-qrd", "qcom,apq8998", "qcom,qrd";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 QRD";
+ compatible = "qcom,apq8098-qrd", "qcom,apq8098", "qcom,qrd";
qcom,board-id = <11 0>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1.dtsi b/arch/arm/boot/dts/qcom/apq8098-v2.1.dtsi
index c7d44816c7d6..a5fa81b71537 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2.1.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8098-v2.1.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,6 @@
#include "msm8998-v2.1.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2.1";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2.1";
qcom,msm-id = <319 0x20001>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.dtsi b/arch/arm/boot/dts/qcom/apq8098-v2.dtsi
index 2be3db45bf37..e51e310f7131 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8098-v2.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,6 @@
#include "msm8998-v2.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998 V2";
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2";
qcom,msm-id = <319 0x20000>;
};
diff --git a/arch/arm/boot/dts/qcom/apq8998.dtsi b/arch/arm/boot/dts/qcom/apq8098.dtsi
index 99d3459e39ce..8cb0c6b17689 100644
--- a/arch/arm/boot/dts/qcom/apq8998.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8098.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,7 +13,7 @@
#include "msm8998.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. APQ 8998";
+ model = "Qualcomm Technologies, Inc. APQ 8098";
qcom,msm-id = <319 0x10000>;
};
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi
index 62115cf6f98a..0ff02042600d 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi
@@ -15,6 +15,7 @@
qcom,mdss-dsi-panel-name = "JDI a407 wqhd cmd mode dsi panel";
qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-panel-clockrate = <838600000>;
qcom,mdss-dsi-virtual-channel-id = <0>;
qcom,mdss-dsi-stream = <0>;
qcom,mdss-dsi-panel-width = <720>;
@@ -79,6 +80,8 @@
qcom,adjust-timer-wakeup-ms = <1>;
qcom,mdss-dsi-reset-sequence = <1 20>, <0 10>, <1 20>;
+ qcom,dcs-cmd-by-left;
+
qcom,config-select = <&dsi_dual_jdi_a407_cmd_config0>;
dsi_dual_jdi_a407_cmd_config0: config0 {
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
index 49ae94e7a975..7d3e3d362946 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
@@ -52,8 +52,12 @@
15 01 00 00 00 00 02 28 40
15 01 00 00 02 00 02 29 4f
15 01 00 00 00 00 02 fe 04
+ 15 01 00 00 00 00 02 0a d8
+ 15 01 00 00 00 00 02 0c e6
+ 15 01 00 00 00 00 02 4e 20
15 01 00 00 00 00 02 4f 1b
- 15 01 00 00 02 00 02 50 2f
+ 15 01 00 00 00 00 02 50 2f
+ 15 01 00 00 02 00 02 51 08
15 01 00 00 00 00 02 fe 09
15 01 00 00 00 00 02 00 08
15 01 00 00 00 00 02 01 08
diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi
index 5c0010d26a8a..9bdaedd72585 100644
--- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi
@@ -41,6 +41,7 @@
interrupt-names = "kpdpwr", "resin",
"resin-bark", "kpdpwr-resin-bark";
qcom,pon-dbc-delay = <15625>;
+ qcom,kpdpwr-sw-debounce;
qcom,system-reset;
qcom,store-hard-reset-reason;
@@ -539,8 +540,10 @@
#address-cells = <1>;
#size-cells = <1>;
qcom,pmic-revid = <&pm660_revid>;
- io-channels = <&pm660_rradc 0>;
- io-channel-names = "rradc_batt_id";
+ io-channels = <&pm660_rradc 0>,
+ <&pm660_rradc 7>;
+ io-channel-names = "rradc_batt_id",
+ "rradc_die_temp";
qcom,rradc-base = <0x4500>;
qcom,fg-esr-timer-awake = <96>;
qcom,fg-esr-timer-asleep = <256>;
diff --git a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
index fdc04b9726b4..679149a78833 100644
--- a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
@@ -415,7 +415,9 @@
compatible = "qcom,qpnp-oledb-regulator";
#address-cells = <1>;
#size-cells = <1>;
+ qcom,pmic-revid = <&pm660l_revid>;
reg = <0xe000 0x100>;
+ qcom,pbs-client = <&pm660l_pbs>;
label = "oledb";
regulator-name = "regulator-oledb";
@@ -463,6 +465,8 @@
qcom,qpnp-lab-slew-rate = <5000>;
qcom,qpnp-lab-init-voltage = <4600000>;
qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+
+ qcom,notify-lab-vreg-ok-sts;
};
};
};
diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
index d007e8bcfc33..c1fa33f5ca32 100644
--- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
@@ -616,12 +616,13 @@
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
- <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
- <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>,
- <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>,
- <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>,
- <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
- <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
+ <&dai_tert_tdm_rx_4>, <&dai_tert_tdm_tx_0>,
+ <&dai_tert_tdm_tx_1>, <&dai_tert_tdm_tx_2>,
+ <&dai_tert_tdm_tx_3>, <&dai_quat_tdm_rx_0>,
+ <&dai_quat_tdm_rx_1>, <&dai_quat_tdm_rx_2>,
+ <&dai_quat_tdm_rx_3>, <&dai_quat_tdm_tx_0>,
+ <&dai_quat_tdm_tx_1>, <&dai_quat_tdm_tx_2>,
+ <&dai_quat_tdm_tx_3>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
"msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
@@ -633,12 +634,13 @@
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
- "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
- "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903",
- "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914",
- "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918",
- "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915",
- "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919";
+ "msm-dai-q6-tdm.36904", "msm-dai-q6-tdm.36897",
+ "msm-dai-q6-tdm.36899", "msm-dai-q6-tdm.36901",
+ "msm-dai-q6-tdm.36903", "msm-dai-q6-tdm.36912",
+ "msm-dai-q6-tdm.36914", "msm-dai-q6-tdm.36916",
+ "msm-dai-q6-tdm.36918", "msm-dai-q6-tdm.36913",
+ "msm-dai-q6-tdm.36915", "msm-dai-q6-tdm.36917",
+ "msm-dai-q6-tdm.36919";
asoc-codec = <&stub_codec>;
asoc-codec-names = "msm-stub-codec.1";
};
@@ -678,9 +680,22 @@
};
qcom,msm-dai-tdm-tert-rx {
+ qcom,msm-cpudai-tdm-group-num-ports = <5>;
+ qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900
+ 36902 36904>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&tert_tdm_dout_active>;
pinctrl-1 = <&tert_tdm_dout_sleep>;
+ dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36904>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
};
qcom,msm-dai-tdm-quat-rx {
diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
index 7fffe81c7614..a5a6bad16176 100644
--- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
@@ -637,12 +637,13 @@
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
- <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
- <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>,
- <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>,
- <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>,
- <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
- <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
+ <&dai_tert_tdm_rx_4>, <&dai_tert_tdm_tx_0>,
+ <&dai_tert_tdm_tx_1>, <&dai_tert_tdm_tx_2>,
+ <&dai_tert_tdm_tx_3>, <&dai_quat_tdm_rx_0>,
+ <&dai_quat_tdm_rx_1>, <&dai_quat_tdm_rx_2>,
+ <&dai_quat_tdm_rx_3>, <&dai_quat_tdm_tx_0>,
+ <&dai_quat_tdm_tx_1>, <&dai_quat_tdm_tx_2>,
+ <&dai_quat_tdm_tx_3>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
"msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
@@ -654,12 +655,13 @@
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
- "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
- "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903",
- "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914",
- "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918",
- "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915",
- "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919";
+ "msm-dai-q6-tdm.36904", "msm-dai-q6-tdm.36897",
+ "msm-dai-q6-tdm.36899", "msm-dai-q6-tdm.36901",
+ "msm-dai-q6-tdm.36903", "msm-dai-q6-tdm.36912",
+ "msm-dai-q6-tdm.36914", "msm-dai-q6-tdm.36916",
+ "msm-dai-q6-tdm.36918", "msm-dai-q6-tdm.36913",
+ "msm-dai-q6-tdm.36915", "msm-dai-q6-tdm.36917",
+ "msm-dai-q6-tdm.36919";
asoc-codec = <&stub_codec>;
asoc-codec-names = "msm-stub-codec.1";
};
@@ -709,6 +711,25 @@
};
};
+ qcom,msm-dai-tdm-tert-rx {
+ qcom,msm-cpudai-tdm-group-num-ports = <5>;
+ qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900
+ 36902 36904>;
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&tert_tdm_dout_active>;
+ pinctrl-1 = <&tert_tdm_dout_sleep>;
+ dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36904>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
qcom,msm-dai-tdm-quat-rx {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&quat_tdm_dout_active>;
diff --git a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
index 215608959dc5..27f692bb14af 100644
--- a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -89,6 +89,13 @@
coresight-child-list = <&funnel_in0>;
coresight-child-ports = <4>;
+ /* DRM settings */
+ qcom,gpmu-tsens = <0x00060007>;
+ qcom,lm-max-power = <5448>;
+ qcom,gpmu-firmware = "a530v3_gpmu.fw2";
+ qcom,gpmu-version = <1 0>;
+ qcom,zap-shader = "a530_zap";
+
clocks = <&clock_gpu clk_gpu_gx_gfx3d_clk>,
<&clock_gpu clk_gpu_ahb_clk>,
<&clock_gpu clk_gpu_gx_rbbmtimer_clk>,
diff --git a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
index b6e6fb4193b4..c5b6e7d0a3dc 100644
--- a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
@@ -542,12 +542,13 @@
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
- <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
- <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>,
- <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>,
- <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>,
- <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
- <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
+ <&dai_tert_tdm_rx_4>, <&dai_tert_tdm_tx_0>,
+ <&dai_tert_tdm_tx_1>, <&dai_tert_tdm_tx_2>,
+ <&dai_tert_tdm_tx_3>, <&dai_quat_tdm_rx_0>,
+ <&dai_quat_tdm_rx_1>, <&dai_quat_tdm_rx_2>,
+ <&dai_quat_tdm_rx_3>, <&dai_quat_tdm_tx_0>,
+ <&dai_quat_tdm_tx_1>, <&dai_quat_tdm_tx_2>,
+ <&dai_quat_tdm_tx_3>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
"msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
@@ -559,12 +560,13 @@
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
- "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
- "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903",
- "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914",
- "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918",
- "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915",
- "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919";
+ "msm-dai-q6-tdm.36904", "msm-dai-q6-tdm.36897",
+ "msm-dai-q6-tdm.36899", "msm-dai-q6-tdm.36901",
+ "msm-dai-q6-tdm.36903", "msm-dai-q6-tdm.36912",
+ "msm-dai-q6-tdm.36914", "msm-dai-q6-tdm.36916",
+ "msm-dai-q6-tdm.36918", "msm-dai-q6-tdm.36913",
+ "msm-dai-q6-tdm.36915", "msm-dai-q6-tdm.36917",
+ "msm-dai-q6-tdm.36919";
asoc-codec = <&stub_codec>;
asoc-codec-names = "msm-stub-codec.1";
};
@@ -592,6 +594,25 @@
};
};
+ qcom,msm-dai-tdm-tert-rx {
+ qcom,msm-cpudai-tdm-group-num-ports = <5>;
+ qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900
+ 36902 36904>;
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&tert_tdm_dout_active>;
+ pinctrl-1 = <&tert_tdm_dout_sleep>;
+ dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36904>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
qcom,msm-dai-tdm-quat-rx {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&quat_tdm_dout_active>;
diff --git a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
index 8aebac3b0e22..cb33df82da0d 100644
--- a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
@@ -51,6 +51,8 @@
#interrupt-cells = <1>;
iommus = <&mdp_smmu 0>;
+ gpus = <&msm_gpu>;
+
/* hw blocks */
qcom,sde-off = <0x1000>;
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index c73a2ff23369..398aee9d3ab8 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -1284,50 +1284,50 @@
39 40 41 42 43>;
#interrupt-cells = <1>;
interrupt-map-mask = <0x0 0x0 0x0 0xffffffff>;
- interrupt-map = <0x0 0x0 0x0 0 &intc 0 405 0
- 0x0 0x0 0x0 1 &intc 0 244 0
- 0x0 0x0 0x0 2 &intc 0 245 0
- 0x0 0x0 0x0 3 &intc 0 247 0
- 0x0 0x0 0x0 4 &intc 0 248 0
- 0x0 0x0 0x0 5 &intc 0 249 0
- 0x0 0x0 0x0 6 &intc 0 250 0
- 0x0 0x0 0x0 7 &intc 0 251 0
- 0x0 0x0 0x0 8 &intc 0 252 0
- 0x0 0x0 0x0 9 &intc 0 253 0
- 0x0 0x0 0x0 10 &intc 0 254 0
- 0x0 0x0 0x0 11 &intc 0 255 0
- 0x0 0x0 0x0 12 &intc 0 512 0
- 0x0 0x0 0x0 13 &intc 0 513 0
- 0x0 0x0 0x0 14 &intc 0 514 0
- 0x0 0x0 0x0 15 &intc 0 515 0
- 0x0 0x0 0x0 16 &intc 0 516 0
- 0x0 0x0 0x0 17 &intc 0 517 0
- 0x0 0x0 0x0 18 &intc 0 518 0
- 0x0 0x0 0x0 19 &intc 0 519 0
- 0x0 0x0 0x0 20 &intc 0 520 0
- 0x0 0x0 0x0 21 &intc 0 521 0
- 0x0 0x0 0x0 22 &intc 0 522 0
- 0x0 0x0 0x0 23 &intc 0 523 0
- 0x0 0x0 0x0 24 &intc 0 524 0
- 0x0 0x0 0x0 25 &intc 0 525 0
- 0x0 0x0 0x0 26 &intc 0 526 0
- 0x0 0x0 0x0 27 &intc 0 527 0
- 0x0 0x0 0x0 28 &intc 0 528 0
- 0x0 0x0 0x0 29 &intc 0 529 0
- 0x0 0x0 0x0 30 &intc 0 530 0
- 0x0 0x0 0x0 31 &intc 0 531 0
- 0x0 0x0 0x0 32 &intc 0 532 0
- 0x0 0x0 0x0 33 &intc 0 533 0
- 0x0 0x0 0x0 34 &intc 0 534 0
- 0x0 0x0 0x0 35 &intc 0 535 0
- 0x0 0x0 0x0 36 &intc 0 536 0
- 0x0 0x0 0x0 37 &intc 0 537 0
- 0x0 0x0 0x0 38 &intc 0 538 0
- 0x0 0x0 0x0 39 &intc 0 539 0
- 0x0 0x0 0x0 40 &intc 0 540 0
- 0x0 0x0 0x0 41 &intc 0 541 0
- 0x0 0x0 0x0 42 &intc 0 542 0
- 0x0 0x0 0x0 43 &intc 0 543 0>;
+ interrupt-map = <0x0 0x0 0x0 0 &intc 0 0 405 0
+ 0x0 0x0 0x0 1 &intc 0 0 244 0
+ 0x0 0x0 0x0 2 &intc 0 0 245 0
+ 0x0 0x0 0x0 3 &intc 0 0 247 0
+ 0x0 0x0 0x0 4 &intc 0 0 248 0
+ 0x0 0x0 0x0 5 &intc 0 0 249 0
+ 0x0 0x0 0x0 6 &intc 0 0 250 0
+ 0x0 0x0 0x0 7 &intc 0 0 251 0
+ 0x0 0x0 0x0 8 &intc 0 0 252 0
+ 0x0 0x0 0x0 9 &intc 0 0 253 0
+ 0x0 0x0 0x0 10 &intc 0 0 254 0
+ 0x0 0x0 0x0 11 &intc 0 0 255 0
+ 0x0 0x0 0x0 12 &intc 0 0 512 0
+ 0x0 0x0 0x0 13 &intc 0 0 513 0
+ 0x0 0x0 0x0 14 &intc 0 0 514 0
+ 0x0 0x0 0x0 15 &intc 0 0 515 0
+ 0x0 0x0 0x0 16 &intc 0 0 516 0
+ 0x0 0x0 0x0 17 &intc 0 0 517 0
+ 0x0 0x0 0x0 18 &intc 0 0 518 0
+ 0x0 0x0 0x0 19 &intc 0 0 519 0
+ 0x0 0x0 0x0 20 &intc 0 0 520 0
+ 0x0 0x0 0x0 21 &intc 0 0 521 0
+ 0x0 0x0 0x0 22 &intc 0 0 522 0
+ 0x0 0x0 0x0 23 &intc 0 0 523 0
+ 0x0 0x0 0x0 24 &intc 0 0 524 0
+ 0x0 0x0 0x0 25 &intc 0 0 525 0
+ 0x0 0x0 0x0 26 &intc 0 0 526 0
+ 0x0 0x0 0x0 27 &intc 0 0 527 0
+ 0x0 0x0 0x0 28 &intc 0 0 528 0
+ 0x0 0x0 0x0 29 &intc 0 0 529 0
+ 0x0 0x0 0x0 30 &intc 0 0 530 0
+ 0x0 0x0 0x0 31 &intc 0 0 531 0
+ 0x0 0x0 0x0 32 &intc 0 0 532 0
+ 0x0 0x0 0x0 33 &intc 0 0 533 0
+ 0x0 0x0 0x0 34 &intc 0 0 534 0
+ 0x0 0x0 0x0 35 &intc 0 0 535 0
+ 0x0 0x0 0x0 36 &intc 0 0 536 0
+ 0x0 0x0 0x0 37 &intc 0 0 537 0
+ 0x0 0x0 0x0 38 &intc 0 0 538 0
+ 0x0 0x0 0x0 39 &intc 0 0 539 0
+ 0x0 0x0 0x0 40 &intc 0 0 540 0
+ 0x0 0x0 0x0 41 &intc 0 0 541 0
+ 0x0 0x0 0x0 42 &intc 0 0 542 0
+ 0x0 0x0 0x0 43 &intc 0 0 543 0>;
interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d",
"int_pls_pme", "int_pme_legacy", "int_pls_err",
@@ -1440,50 +1440,50 @@
39 40 41 42 43>;
#interrupt-cells = <1>;
interrupt-map-mask = <0x0 0x0 0x0 0xffffffff>;
- interrupt-map = <0x0 0x0 0x0 0 &intc 0 413 0
- 0x0 0x0 0x0 1 &intc 0 272 0
- 0x0 0x0 0x0 2 &intc 0 273 0
- 0x0 0x0 0x0 3 &intc 0 274 0
- 0x0 0x0 0x0 4 &intc 0 275 0
- 0x0 0x0 0x0 5 &intc 0 276 0
- 0x0 0x0 0x0 6 &intc 0 277 0
- 0x0 0x0 0x0 7 &intc 0 278 0
- 0x0 0x0 0x0 8 &intc 0 279 0
- 0x0 0x0 0x0 9 &intc 0 280 0
- 0x0 0x0 0x0 10 &intc 0 281 0
- 0x0 0x0 0x0 11 &intc 0 282 0
- 0x0 0x0 0x0 12 &intc 0 544 0
- 0x0 0x0 0x0 13 &intc 0 545 0
- 0x0 0x0 0x0 14 &intc 0 546 0
- 0x0 0x0 0x0 15 &intc 0 547 0
- 0x0 0x0 0x0 16 &intc 0 548 0
- 0x0 0x0 0x0 17 &intc 0 549 0
- 0x0 0x0 0x0 18 &intc 0 550 0
- 0x0 0x0 0x0 19 &intc 0 551 0
- 0x0 0x0 0x0 20 &intc 0 552 0
- 0x0 0x0 0x0 21 &intc 0 553 0
- 0x0 0x0 0x0 22 &intc 0 554 0
- 0x0 0x0 0x0 23 &intc 0 555 0
- 0x0 0x0 0x0 24 &intc 0 556 0
- 0x0 0x0 0x0 25 &intc 0 557 0
- 0x0 0x0 0x0 26 &intc 0 558 0
- 0x0 0x0 0x0 27 &intc 0 559 0
- 0x0 0x0 0x0 28 &intc 0 560 0
- 0x0 0x0 0x0 29 &intc 0 561 0
- 0x0 0x0 0x0 30 &intc 0 562 0
- 0x0 0x0 0x0 31 &intc 0 563 0
- 0x0 0x0 0x0 32 &intc 0 564 0
- 0x0 0x0 0x0 33 &intc 0 565 0
- 0x0 0x0 0x0 34 &intc 0 566 0
- 0x0 0x0 0x0 35 &intc 0 567 0
- 0x0 0x0 0x0 36 &intc 0 568 0
- 0x0 0x0 0x0 37 &intc 0 569 0
- 0x0 0x0 0x0 38 &intc 0 570 0
- 0x0 0x0 0x0 39 &intc 0 571 0
- 0x0 0x0 0x0 40 &intc 0 572 0
- 0x0 0x0 0x0 41 &intc 0 573 0
- 0x0 0x0 0x0 42 &intc 0 574 0
- 0x0 0x0 0x0 43 &intc 0 575 0>;
+ interrupt-map = <0x0 0x0 0x0 0 &intc 0 0 413 0
+ 0x0 0x0 0x0 1 &intc 0 0 272 0
+ 0x0 0x0 0x0 2 &intc 0 0 273 0
+ 0x0 0x0 0x0 3 &intc 0 0 274 0
+ 0x0 0x0 0x0 4 &intc 0 0 275 0
+ 0x0 0x0 0x0 5 &intc 0 0 276 0
+ 0x0 0x0 0x0 6 &intc 0 0 277 0
+ 0x0 0x0 0x0 7 &intc 0 0 278 0
+ 0x0 0x0 0x0 8 &intc 0 0 279 0
+ 0x0 0x0 0x0 9 &intc 0 0 280 0
+ 0x0 0x0 0x0 10 &intc 0 0 281 0
+ 0x0 0x0 0x0 11 &intc 0 0 282 0
+ 0x0 0x0 0x0 12 &intc 0 0 544 0
+ 0x0 0x0 0x0 13 &intc 0 0 545 0
+ 0x0 0x0 0x0 14 &intc 0 0 546 0
+ 0x0 0x0 0x0 15 &intc 0 0 547 0
+ 0x0 0x0 0x0 16 &intc 0 0 548 0
+ 0x0 0x0 0x0 17 &intc 0 0 549 0
+ 0x0 0x0 0x0 18 &intc 0 0 550 0
+ 0x0 0x0 0x0 19 &intc 0 0 551 0
+ 0x0 0x0 0x0 20 &intc 0 0 552 0
+ 0x0 0x0 0x0 21 &intc 0 0 553 0
+ 0x0 0x0 0x0 22 &intc 0 0 554 0
+ 0x0 0x0 0x0 23 &intc 0 0 555 0
+ 0x0 0x0 0x0 24 &intc 0 0 556 0
+ 0x0 0x0 0x0 25 &intc 0 0 557 0
+ 0x0 0x0 0x0 26 &intc 0 0 558 0
+ 0x0 0x0 0x0 27 &intc 0 0 559 0
+ 0x0 0x0 0x0 28 &intc 0 0 560 0
+ 0x0 0x0 0x0 29 &intc 0 0 561 0
+ 0x0 0x0 0x0 30 &intc 0 0 562 0
+ 0x0 0x0 0x0 31 &intc 0 0 563 0
+ 0x0 0x0 0x0 32 &intc 0 0 564 0
+ 0x0 0x0 0x0 33 &intc 0 0 565 0
+ 0x0 0x0 0x0 34 &intc 0 0 566 0
+ 0x0 0x0 0x0 35 &intc 0 0 567 0
+ 0x0 0x0 0x0 36 &intc 0 0 568 0
+ 0x0 0x0 0x0 37 &intc 0 0 569 0
+ 0x0 0x0 0x0 38 &intc 0 0 570 0
+ 0x0 0x0 0x0 39 &intc 0 0 571 0
+ 0x0 0x0 0x0 40 &intc 0 0 572 0
+ 0x0 0x0 0x0 41 &intc 0 0 573 0
+ 0x0 0x0 0x0 42 &intc 0 0 574 0
+ 0x0 0x0 0x0 43 &intc 0 0 575 0>;
interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d",
"int_pls_pme", "int_pme_legacy", "int_pls_err",
@@ -1592,50 +1592,50 @@
39 40 41 42 43>;
#interrupt-cells = <1>;
interrupt-map-mask = <0x0 0x0 0x0 0xffffffff>;
- interrupt-map = <0x0 0x0 0x0 0 &intc 0 421 0
- 0x0 0x0 0x0 1 &intc 0 142 0
- 0x0 0x0 0x0 2 &intc 0 143 0
- 0x0 0x0 0x0 3 &intc 0 144 0
- 0x0 0x0 0x0 4 &intc 0 145 0
- 0x0 0x0 0x0 5 &intc 0 146 0
- 0x0 0x0 0x0 6 &intc 0 147 0
- 0x0 0x0 0x0 7 &intc 0 148 0
- 0x0 0x0 0x0 8 &intc 0 149 0
- 0x0 0x0 0x0 9 &intc 0 260 0
- 0x0 0x0 0x0 10 &intc 0 261 0
- 0x0 0x0 0x0 11 &intc 0 262 0
- 0x0 0x0 0x0 12 &intc 0 576 0
- 0x0 0x0 0x0 13 &intc 0 577 0
- 0x0 0x0 0x0 14 &intc 0 578 0
- 0x0 0x0 0x0 15 &intc 0 579 0
- 0x0 0x0 0x0 16 &intc 0 580 0
- 0x0 0x0 0x0 17 &intc 0 581 0
- 0x0 0x0 0x0 18 &intc 0 582 0
- 0x0 0x0 0x0 19 &intc 0 583 0
- 0x0 0x0 0x0 20 &intc 0 584 0
- 0x0 0x0 0x0 21 &intc 0 585 0
- 0x0 0x0 0x0 22 &intc 0 586 0
- 0x0 0x0 0x0 23 &intc 0 587 0
- 0x0 0x0 0x0 24 &intc 0 588 0
- 0x0 0x0 0x0 25 &intc 0 589 0
- 0x0 0x0 0x0 26 &intc 0 590 0
- 0x0 0x0 0x0 27 &intc 0 591 0
- 0x0 0x0 0x0 28 &intc 0 592 0
- 0x0 0x0 0x0 29 &intc 0 593 0
- 0x0 0x0 0x0 30 &intc 0 594 0
- 0x0 0x0 0x0 31 &intc 0 595 0
- 0x0 0x0 0x0 32 &intc 0 596 0
- 0x0 0x0 0x0 33 &intc 0 597 0
- 0x0 0x0 0x0 34 &intc 0 598 0
- 0x0 0x0 0x0 35 &intc 0 599 0
- 0x0 0x0 0x0 36 &intc 0 600 0
- 0x0 0x0 0x0 37 &intc 0 601 0
- 0x0 0x0 0x0 38 &intc 0 602 0
- 0x0 0x0 0x0 39 &intc 0 603 0
- 0x0 0x0 0x0 40 &intc 0 604 0
- 0x0 0x0 0x0 41 &intc 0 605 0
- 0x0 0x0 0x0 42 &intc 0 606 0
- 0x0 0x0 0x0 43 &intc 0 607 0>;
+ interrupt-map = <0x0 0x0 0x0 0 &intc 0 0 421 0
+ 0x0 0x0 0x0 1 &intc 0 0 142 0
+ 0x0 0x0 0x0 2 &intc 0 0 143 0
+ 0x0 0x0 0x0 3 &intc 0 0 144 0
+ 0x0 0x0 0x0 4 &intc 0 0 145 0
+ 0x0 0x0 0x0 5 &intc 0 0 146 0
+ 0x0 0x0 0x0 6 &intc 0 0 147 0
+ 0x0 0x0 0x0 7 &intc 0 0 148 0
+ 0x0 0x0 0x0 8 &intc 0 0 149 0
+ 0x0 0x0 0x0 9 &intc 0 0 260 0
+ 0x0 0x0 0x0 10 &intc 0 0 261 0
+ 0x0 0x0 0x0 11 &intc 0 0 262 0
+ 0x0 0x0 0x0 12 &intc 0 0 576 0
+ 0x0 0x0 0x0 13 &intc 0 0 577 0
+ 0x0 0x0 0x0 14 &intc 0 0 578 0
+ 0x0 0x0 0x0 15 &intc 0 0 579 0
+ 0x0 0x0 0x0 16 &intc 0 0 580 0
+ 0x0 0x0 0x0 17 &intc 0 0 581 0
+ 0x0 0x0 0x0 18 &intc 0 0 582 0
+ 0x0 0x0 0x0 19 &intc 0 0 583 0
+ 0x0 0x0 0x0 20 &intc 0 0 584 0
+ 0x0 0x0 0x0 21 &intc 0 0 585 0
+ 0x0 0x0 0x0 22 &intc 0 0 586 0
+ 0x0 0x0 0x0 23 &intc 0 0 587 0
+ 0x0 0x0 0x0 24 &intc 0 0 588 0
+ 0x0 0x0 0x0 25 &intc 0 0 589 0
+ 0x0 0x0 0x0 26 &intc 0 0 590 0
+ 0x0 0x0 0x0 27 &intc 0 0 591 0
+ 0x0 0x0 0x0 28 &intc 0 0 592 0
+ 0x0 0x0 0x0 29 &intc 0 0 593 0
+ 0x0 0x0 0x0 30 &intc 0 0 594 0
+ 0x0 0x0 0x0 31 &intc 0 0 595 0
+ 0x0 0x0 0x0 32 &intc 0 0 596 0
+ 0x0 0x0 0x0 33 &intc 0 0 597 0
+ 0x0 0x0 0x0 34 &intc 0 0 598 0
+ 0x0 0x0 0x0 35 &intc 0 0 599 0
+ 0x0 0x0 0x0 36 &intc 0 0 600 0
+ 0x0 0x0 0x0 37 &intc 0 0 601 0
+ 0x0 0x0 0x0 38 &intc 0 0 602 0
+ 0x0 0x0 0x0 39 &intc 0 0 603 0
+ 0x0 0x0 0x0 40 &intc 0 0 604 0
+ 0x0 0x0 0x0 41 &intc 0 0 605 0
+ 0x0 0x0 0x0 42 &intc 0 0 606 0
+ 0x0 0x0 0x0 43 &intc 0 0 607 0>;
interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d",
"int_pls_pme", "int_pme_legacy", "int_pls_err",
diff --git a/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts
new file mode 100644
index 000000000000..5fe0d0176618
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "msm8998-cdp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v1 CDP";
+ compatible = "qcom,msm8998-cdp", "qcom,msm8998", "qcom,cdp";
+ qcom,msm-id = <292 0x0>;
+ qcom,board-id = <1 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi
index 2b925250c5c0..9f57fa5127d7 100644
--- a/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi
@@ -10,9 +10,8 @@
* GNU General Public License for more details.
*/
-#include "msm8998-pinctrl.dtsi"
#include "msm8998-camera-sensor-cdp.dtsi"
-/ {
+&vendor {
bluetooth: bt_wcn3990 {
compatible = "qca,wcn3990";
qca,bt-vdd-io-supply = <&pm8998_s3>;
diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
index d0d13332595a..64f377f1a576 100644
--- a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
@@ -10,8 +10,6 @@
* GNU General Public License for more details.
*/
-#include "dsi-panel-sim-video.dtsi"
-#include "dsi-panel-sim-dualmipi-video.dtsi"
#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi"
#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi"
#include "dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi
index 24186aca22be..2b9e13ea24f2 100644
--- a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi
@@ -502,7 +502,16 @@
qcom,msm_ext_disp = <&msm_ext_disp>;
- qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03];
+ qcom,aux-cfg0-settings = [1c 00];
+ qcom,aux-cfg1-settings = [20 13 23 1d];
+ qcom,aux-cfg2-settings = [24 00];
+ qcom,aux-cfg3-settings = [28 00];
+ qcom,aux-cfg4-settings = [2c 0a];
+ qcom,aux-cfg5-settings = [30 26];
+ qcom,aux-cfg6-settings = [34 0a];
+ qcom,aux-cfg7-settings = [38 03];
+ qcom,aux-cfg8-settings = [3c bb];
+ qcom,aux-cfg9-settings = [40 03];
qcom,logical2physical-lane-map = [02 03 01 00];
qcom,core-supply-entries {
diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts
new file mode 100644
index 000000000000..8525d781aae2
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "msm8998-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v1 MTP";
+ compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp";
+ qcom,msm-id = <292 0x0>;
+ qcom,board-id = <8 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi
index f7dcfc7c149f..859e77679e44 100644
--- a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi
@@ -11,9 +11,8 @@
*/
#include <dt-bindings/interrupt-controller/irq.h>
-#include "msm8998-pinctrl.dtsi"
#include "msm8998-camera-sensor-mtp.dtsi"
-/ {
+&vendor {
bluetooth: bt_wcn3990 {
compatible = "qca,wcn3990";
qca,bt-vdd-io-supply = <&pm8998_s3>;
@@ -586,7 +585,7 @@
};
};
-/{
+&vendor {
mtp_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>;
#include "fg-gen3-batterydata-itech-3000mah.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts
new file mode 100644
index 000000000000..55255261a827
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "msm8998-qrd.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 QRD";
+ compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
+ qcom,msm-id = <292 0x0>;
+ qcom,board-id = <11 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts
new file mode 100644
index 000000000000..408a067dbeee
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "msm8998-qrd-skuk.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 QRD SKUK";
+ compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
+ qcom,msm-id = <292 0x0>;
+ qcom,board-id = <0x01000b 0x80>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi
index bc87b375ff50..97c4c5b1d455 100644
--- a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi
@@ -289,7 +289,7 @@
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
-/{
+&vendor {
qrd_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>;
#include "fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts
new file mode 100644
index 000000000000..ff0e24dd0371
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "msm8998-qrd-vr1.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 QRD VR1 Board";
+ compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
+ qcom,msm-id = <292 0x0>;
+ qcom,board-id = <0x02000b 0x80>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi
index aa95872da385..0d0c66d7f26e 100644
--- a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi
@@ -219,7 +219,7 @@
};
};
-/{
+&vendor {
qrd_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>;
#include "fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi
index 1c2db6833bde..a3eb3e5ab0d0 100644
--- a/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi
@@ -539,7 +539,7 @@
};
};
-/{
+&vendor {
qrd_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>;
#include "fg-gen3-batterydata-itech-3000mah.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts
new file mode 100644
index 000000000000..c602459815b2
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "msm8998-cdp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v2 CDP";
+ compatible = "qcom,msm8998-cdp", "qcom,msm8998", "qcom,cdp";
+ qcom,msm-id = <292 0x20000>;
+ qcom,board-id = <1 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts
new file mode 100644
index 000000000000..660fc4d51f9f
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "msm8998-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v2 MTP";
+ compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp";
+ qcom,msm-id = <292 0x20000>;
+ qcom,board-id = <8 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts
new file mode 100644
index 000000000000..47c55a045017
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "msm8998-cdp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 CDP";
+ compatible = "qcom,msm8998-cdp", "qcom,msm8998", "qcom,cdp";
+ qcom,msm-id = <292 0x20001>;
+ qcom,board-id = <1 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts
new file mode 100644
index 000000000000..7d537aa35533
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts
@@ -0,0 +1,51 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "msm8998-v2.1.dtsi"
+#include "msm8998-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP, 4k display";
+ compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp";
+ qcom,board-id = <8 4>;
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "split_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_sharp_4k_dsc_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,panel-mode-gpio = <&tlmm 91 0>;
+};
+
+&mdss_dsi1 {
+ qcom,dsi-pref-prim-pan = <&dsi_sharp_4k_dsc_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,panel-mode-gpio = <&tlmm 91 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts
new file mode 100644
index 000000000000..54a0e98cdce7
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "msm8998-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP";
+ compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp";
+ qcom,msm-id = <292 0x20001>;
+ qcom,board-id = <8 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1.dts
new file mode 100644
index 000000000000..64559c71307f
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-v2.1.dts
@@ -0,0 +1,23 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include "msm8998-v2.1.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 SoC";
+ compatible = "qcom,msm8998";
+ qcom,board-id = <0 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.dts b/arch/arm/boot/dts/qcom/msm8998-v2.dts
new file mode 100644
index 000000000000..f1b5af9f14c8
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-v2.dts
@@ -0,0 +1,23 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include "msm8998-v2.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v2 SoC";
+ compatible = "qcom,msm8998";
+ qcom,board-id = <0 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi
index 348faf9b69c3..b2f30de94bbc 100644
--- a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi
@@ -591,53 +591,53 @@
qcom,cpr-open-loop-voltage-adjustment =
/* Speed bin 0 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-12000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-12000) (-12000) (-12000) (-12000)
(-12000) (-16000) (-16000) (-20000) (-24000)
(-28000) (-28000)>,
/* Speed bin 1 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-12000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-12000) (-12000) (-12000) (-12000)
(-12000) (-16000) (-16000) (-20000) (-24000)
(-28000) (-28000)>,
/* Speed bin 2 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-12000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-12000) (-12000) (-12000) (-12000)
(-12000) (-16000) (-16000) (-20000) (-24000)
(-28000) (-28000)>,
/* Speed bin 3 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-12000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-12000) (-12000) (-12000) (-12000)
(-12000) (-16000) (-16000) (-20000) (-24000)
(-28000) (-28000)>;
qcom,cpr-closed-loop-voltage-adjustment =
/* Speed bin 0 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-7000) (-8000)
- (-10000) (-10000) (-11000) (-12000) (-13000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-11000) (-12000) (-13000)
(-14000) (-14000) (-15000) (-21000) (-24000)
(-26000) (-28000)>,
/* Speed bin 1 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-7000) (-8000)
- (-10000) (-10000) (-11000) (-12000) (-13000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-11000) (-12000) (-13000)
(-14000) (-14000) (-15000) (-21000) (-24000)
(-26000) (-28000)>,
/* Speed bin 2 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-7000) (-8000)
- (-10000) (-10000) (-11000) (-12000) (-13000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-11000) (-12000) (-13000)
(-14000) (-14000) (-15000) (-21000) (-24000)
(-26000) (-28000)>,
/* Speed bin 3 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-7000) (-8000)
- (-10000) (-10000) (-11000) (-12000) (-13000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-11000) (-12000) (-13000)
(-14000) (-14000) (-15000) (-21000) (-24000)
(-26000) (-28000)>;
@@ -926,65 +926,65 @@
qcom,cpr-open-loop-voltage-adjustment =
/* Speed bin 0 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-8000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-8000) (-12000) (-12000) (-12000)
(-12000) (-12000) (-12000) (-16000) (-16000)
- (-20000) (-20000) (-24000) (-24000) (-24000)
+ (-20000) (-16000) (-16000) (-16000) (-12000)
(-28000) (-28000) (-28000) (-28000) (-28000)
(-28000) (-28000)>,
/* Speed bin 1 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-8000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-8000) (-12000) (-12000) (-12000)
(-12000) (-12000) (-12000) (-16000) (-16000)
- (-20000) (-20000) (-24000) (-24000) (-28000)
+ (-20000) (-16000) (-16000) (-16000) (-16000)
(-28000)>,
/* Speed bin 2 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-8000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-8000) (-12000) (-12000) (-12000)
(-12000) (-12000) (-12000) (-16000) (-16000)
- (-20000) (-20000) (-24000) (-24000) (-24000)
+ (-20000) (-16000) (-16000) (-16000) (-12000)
(-28000) (-28000) (-28000) (-28000) (-28000)>,
/* Speed bin 3 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-8000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-8000) (-12000) (-12000) (-12000)
(-12000) (-12000) (-12000) (-16000) (-16000)
- (-20000) (-20000) (-24000) (-24000) (-24000)
+ (-20000) (-16000) (-16000) (-16000) (-12000)
(-28000) (-28000) (-28000) (-28000) (-28000)
(-28000)>;
qcom,cpr-closed-loop-voltage-adjustment =
/* Speed bin 0 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-6000) (-7000)
- (-9000) (-10000) (-10000) (-11000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-10000) (-11000) (-12000)
(-12000) (-13000) (-14000) (-14000) (-15000)
- (-18000) (-21000) (-24000) (-25000) (-25000)
+ (-16000) (-16000) (-17000) (-15000) (-13000)
(-26000) (-26000) (-27000) (-27000) (-28000)
(-28000) (-28000)>,
/* Speed bin 1 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-6000) (-7000)
- (-9000) (-10000) (-10000) (-11000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-10000) (-11000) (-12000)
(-12000) (-13000) (-14000) (-14000) (-15000)
- (-18000) (-21000) (-24000) (-26000) (-27000)
+ (-16000) (-16000) (-17000) (-16000) (-15000)
(-28000)>,
/* Speed bin 2 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-6000) (-7000)
- (-9000) (-10000) (-10000) (-11000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-10000) (-11000) (-12000)
(-12000) (-13000) (-14000) (-14000) (-15000)
- (-18000) (-21000) (-24000) (-25000) (-26000)
+ (-16000) (-16000) (-17000) (-15000) (-14000)
(-27000) (-27000) (-28000) (-28000) (-28000)>,
/* Speed bin 3 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-6000) (-7000)
- (-9000) (-10000) (-10000) (-11000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-10000) (-11000) (-12000)
(-12000) (-13000) (-14000) (-14000) (-15000)
- (-18000) (-21000) (-24000) (-25000) (-26000)
+ (-16000) (-16000) (-17000) (-15000) (-14000)
(-26000) (-27000) (-27000) (-28000) (-28000)
(-28000)>;
diff --git a/arch/arm/boot/dts/qcom/msm8998.dts b/arch/arm/boot/dts/qcom/msm8998.dts
new file mode 100644
index 000000000000..b340b6de4c00
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998.dts
@@ -0,0 +1,23 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include "msm8998.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v1 SoC";
+ compatible = "qcom,msm8998";
+ qcom,board-id = <0 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi
index 7a0ed2497a7e..9b5092cf7f14 100644
--- a/arch/arm/boot/dts/qcom/msm8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998.dtsi
@@ -275,6 +275,13 @@
soc: soc { };
+ vendor: vendor {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0xffffffff>;
+ compatible = "simple-bus";
+ };
+
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
@@ -434,6 +441,7 @@
interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>;
qcom,ee = <0>;
qcom,channel = <0>;
+ qcom,reserved-chan = <511>;
#address-cells = <2>;
#size-cells = <0>;
interrupt-controller;
diff --git a/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts
new file mode 100644
index 000000000000..480a69601541
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts
@@ -0,0 +1,85 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sda630.dtsi"
+#include "sdm630-qrd.dtsi"
+#include "msm-pm660a.dtsi"
+#include "sdm660-internal-codec.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A QRD HDK630";
+ compatible = "qcom,sda630-qrd", "qcom,sda630", "qcom,qrd";
+ qcom,board-id = <0x0006000b 0x00>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+};
+
+&int_codec {
+ qcom,model = "sdm660-snd-card-skush";
+ /delete-property/ qcom,us-euro-gpios;
+ qcom,audio-routing =
+ "RX_BIAS", "INT_MCLK0",
+ "SPK_RX_BIAS", "INT_MCLK0",
+ "INT_LDO_H", "INT_MCLK0",
+ "MIC BIAS External2", "Headset Mic",
+ "AMIC2", "MIC BIAS External2",
+ "MIC BIAS External", "Digital Mic1",
+ "DMIC1", "MIC BIAS External",
+ "MIC BIAS External", "Digital Mic3",
+ "DMIC3", "MIC BIAS External",
+ "MIC BIAS External", "Digital Mic4",
+ "DMIC4", "MIC BIAS External",
+ "SpkrLeft IN", "SPK1 OUT",
+ "PDM_IN_RX1", "PDM_OUT_RX1",
+ "PDM_IN_RX2", "PDM_OUT_RX2",
+ "PDM_IN_RX3", "PDM_OUT_RX3",
+ "ADC1_IN", "ADC1_OUT",
+ "ADC2_IN", "ADC2_OUT",
+ "ADC3_IN", "ADC3_OUT";
+ qcom,wsa-max-devs = <1>;
+ qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_213_en>;
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
+};
+
+&pm660a_oledb {
+ status = "okay";
+ qcom,oledb-default-voltage-mv = <6400>;
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ oledb-supply = <&pm660a_oledb>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+ qcom,platform-reset-gpio = <&tlmm 53 0>;
+ qcom,platform-te-gpio = <&tlmm 59 0>;
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <255>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+};
diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts
new file mode 100644
index 000000000000..a767853cd829
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts
@@ -0,0 +1,56 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sda660.dtsi"
+#include "sdm660-qrd.dtsi"
+#include "msm-pm660a.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA 660 PM660 + PM660A QRD HDK660";
+ compatible = "qcom,sda660-qrd", "qcom,sda660", "qcom,qrd";
+ qcom,board-id = <0x0006000b 0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+};
+
+&pm660a_oledb {
+ status = "okay";
+ qcom,oledb-default-voltage-mv = <6400>;
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+ qcom,platform-reset-gpio = <&tlmm 53 0>;
+ qcom,platform-te-gpio = <&tlmm 59 0>;
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <255>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi
index 55e6943bf327..fd109450ad81 100644
--- a/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi
@@ -192,3 +192,30 @@
qcom,partial-update-enabled = "single_roi";
qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
};
+
+&pm660l_pwm_4 {
+ qcom,dtest-line = <2>; /* DTEST2 */
+ qcom,dtest-output = <2>; /* OUTPUT PWM */
+};
+
+&pm660l_gpios {
+ gpio@c500 {
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,src-sel = <7>; /* DTEST2 */
+ qcom,master-en = <1>; /* Enable MPP */
+ qcom,invert = <0>; /* Enable MPP */
+ };
+};
+
+&dsi_sharp_split_link_wuxga_video {
+ pwms = <&pm660l_pwm_4 0 0>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pwm-pmi;
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,panel-supply-entries = <&dsi_panel_split_link_pwr_supply>;
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi
index dfed9ec80a34..4e3ebd445814 100644
--- a/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi
@@ -16,6 +16,7 @@
#include "dsi-panel-truly-1080p-cmd.dtsi"
#include "dsi-panel-truly-1080p-video.dtsi"
#include "dsi-panel-rm67195-amoled-fhd-cmd.dtsi"
+#include "dsi-panel-sharp-split-link-wuxga-video.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -51,6 +52,48 @@
};
};
+ dsi_panel_split_link_pwr_supply: dsi_panel_split_link_pwr_supply {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "wqhd-vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1950000>;
+ qcom,supply-enable-load = <32000>;
+ qcom,supply-disable-load = <80>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vdda-3p3";
+ qcom,supply-min-voltage = <3300000>;
+ qcom,supply-max-voltage = <3300000>;
+ qcom,supply-enable-load = <13900>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "lab";
+ qcom,supply-min-voltage = <5500000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@3 {
+ reg = <3>;
+ qcom,supply-name = "ibb";
+ qcom,supply-min-voltage = <5500000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <10>;
+ };
+ };
+
dsi_panel_pwr_supply_labibb_amoled:
dsi_panel_pwr_supply_labibb_amoled {
#address-cells = <1>;
@@ -114,7 +157,14 @@
qcom,mdss-dsi-pan-enable-dynamic-fps;
qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+
};
&dsi_nt35695b_truly_fhd_cmd {
@@ -124,7 +174,14 @@
24 1e 08 09 05 03 04 a0
24 1a 08 09 05 03 04 a0];
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+
};
&dsi_truly_1080_vid {
@@ -138,7 +195,14 @@
qcom,mdss-dsi-pan-enable-dynamic-fps;
qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x1c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x1c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+
};
&dsi_truly_1080_cmd {
@@ -148,7 +212,14 @@
23 1e 08 09 05 03 04 a0
23 1a 08 09 05 03 04 a0];
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x1c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x1c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+
};
&dsi_rm67195_amoled_fhd_cmd {
@@ -160,3 +231,15 @@
qcom,mdss-dsi-t-clk-post = <0x0d>;
qcom,mdss-dsi-t-clk-pre = <0x2f>;
};
+
+&dsi_sharp_split_link_wuxga_video {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [25 1f 09 0a 06 03 04 a0
+ 25 1f 09 0a 06 03 04 a0
+ 25 1f 09 0a 06 03 04 a0
+ 25 1f 09 0a 06 03 04 a0
+ 25 1f 08 0a 06 03 04 a0];
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+};
diff --git a/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi
index 0619d62526b1..bb50f23d8126 100644
--- a/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi
@@ -190,3 +190,19 @@
qcom,mdss-dsi-bl-max-level = <4095>;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
+
+&i2c_2 {
+ status = "ok";
+ smb1351-charger@1d {
+ compatible = "qcom,smb1351-charger";
+ reg = <0x1d>;
+ qcom,parallel-charger;
+ qcom,float-voltage-mv = <4400>;
+ qcom,recharge-mv = <100>;
+ qcom,parallel-en-pin-polarity = <1>;
+ };
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts
index d9b6a8ae9d34..7e3e9a0cca59 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts
@@ -34,3 +34,10 @@
qcom,msm-mbhc-hphl-swh = <0>;
qcom,msm-mbhc-gnd-swh = <0>;
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>;
+ oledb-supply = <&pm660a_oledb>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts
index 8ebdbc08a00c..a522b7ad1d5f 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts
@@ -28,3 +28,10 @@
&tavil_snd {
qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>;
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>;
+ oledb-supply = <&pm660a_oledb>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi
index fb24f727fb49..c677df529dd7 100644
--- a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi
@@ -22,11 +22,13 @@
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&tlmm>;
- interrupts = <21 0x0>;
+ interrupts = <21 IRQ_TYPE_LEVEL_LOW>;
interrupt_names = "smb138x";
interrupt-controller;
#interrupt-cells = <3>;
qcom,periph-map = <0x10 0x11 0x12 0x13 0x14 0x16 0x36>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default>;
smb138x_revid: qcom,revid@100 {
compatible = "qcom,qpnp-revid";
@@ -131,6 +133,21 @@
};
};
+&tlmm {
+ smb_int_default: smb_int_default {
+ mux {
+ pins = "gpio21";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio21";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+};
+
/ {
qrd_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>;
diff --git a/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi
index eded8b08528a..c4adcfe1bdfb 100644
--- a/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi
@@ -28,7 +28,7 @@
rpm-regulator-smpa5 {
status = "okay";
pm660_s5: regulator-s5 {
- regulator-min-microvolt = <1350000>;
+ regulator-min-microvolt = <1224000>;
regulator-max-microvolt = <1350000>;
status = "okay";
};
@@ -657,6 +657,7 @@
qcom,cpr-up-error-step-limit = <1>;
qcom,cpr-corner-switch-delay-time = <1042>;
qcom,cpr-voltage-settling-time = <1760>;
+ qcom,cpr-reset-step-quot-loop-en;
qcom,apm-threshold-voltage = <872000>;
qcom,apm-crossover-voltage = <872000>;
@@ -665,6 +666,9 @@
qcom,voltage-base = <400000>;
qcom,cpr-saw-use-unit-mV;
+ qcom,cpr-enable;
+ qcom,cpr-hw-closed-loop;
+
qcom,cpr-panic-reg-addr-list =
<0x179cbaa4 0x17912c18>;
qcom,cpr-panic-reg-name-list =
@@ -705,6 +709,24 @@
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+ qcom,cpr-ro-scaling-factor =
+ <3600 3600 3830 2430 2520 2700 1790
+ 1760 1970 1880 2110 2010 2510 4900
+ 4370 4780>,
+ <3600 3600 3830 2430 2520 2700 1790
+ 1760 1970 1880 2110 2010 2510 4900
+ 4370 4780>,
+ <3600 3600 3830 2430 2520 2700 1790
+ 1760 1970 1880 2110 2010 2510 4900
+ 4370 4780>;
+
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ <(-30000) (-30000) (-30000)>;
+
+ qcom,cpr-floor-to-ceiling-max-range =
+ <32000 32000 32000 40000 44000
+ 40000 40000 40000>;
};
};
};
@@ -731,6 +753,7 @@
qcom,cpr-up-error-step-limit = <1>;
qcom,cpr-corner-switch-delay-time = <1042>;
qcom,cpr-voltage-settling-time = <1760>;
+ qcom,cpr-reset-step-quot-loop-en;
qcom,apm-threshold-voltage = <872000>;
qcom,apm-crossover-voltage = <872000>;
@@ -739,6 +762,9 @@
qcom,voltage-base = <400000>;
qcom,cpr-saw-use-unit-mV;
+ qcom,cpr-enable;
+ qcom,cpr-hw-closed-loop;
+
qcom,cpr-panic-reg-addr-list =
<0x179c7aa4 0x17812c18>;
qcom,cpr-panic-reg-name-list =
@@ -834,6 +860,46 @@
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+ qcom,cpr-ro-scaling-factor =
+ <4040 4230 0000 2210 2560 2450 2230
+ 2220 2410 2300 2560 2470 1600 3120
+ 2620 2280>,
+ <4040 4230 0000 2210 2560 2450 2230
+ 2220 2410 2300 2560 2470 1600 3120
+ 2620 2280>,
+ <4040 4230 0000 2210 2560 2450 2230
+ 2220 2410 2300 2560 2470 1600 3120
+ 2620 2280>,
+ <4040 4230 0000 2210 2560 2450 2230
+ 2220 2410 2300 2560 2470 1600 3120
+ 2620 2280>,
+ <4040 4230 0000 2210 2560 2450 2230
+ 2220 2410 2300 2560 2470 1600 3120
+ 2620 2280>;
+
+ qcom,cpr-open-loop-voltage-fuse-adjustment =
+ <15000 5000 5000 0 0>;
+
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ <(-30000) (-30000) (-30000)
+ (-30000) (-30000)>;
+
+ qcom,cpr-floor-to-ceiling-max-range =
+ /* Speed bin 0 */
+ <40000 40000 40000 40000
+ 40000 40000 40000 66000
+ 66000 40000>,
+
+ /* Speed bin 1 */
+ <40000 40000 40000 40000
+ 40000 40000 40000 66000
+ 66000 40000>,
+
+ /* Speed bin 2 */
+ <40000 40000 40000 40000
+ 40000 40000 40000 66000
+ 66000 40000 40000>;
};
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi
index 2ef14d766560..24a935ffebec 100644
--- a/arch/arm/boot/dts/qcom/sdm630.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630.dtsi
@@ -1988,12 +1988,12 @@
interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>;
qcom,ee = <0>;
qcom,channel = <0>;
+ qcom,reserved-chan = <511>;
#address-cells = <2>;
#size-cells = <0>;
interrupt-controller;
#interrupt-cells = <4>;
cell-index = <0>;
- qcom,not-wakeup; /* Needed until Full-boot-chain enabled */
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
index 33303f1e2a74..4d05ea75b576 100644
--- a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
@@ -180,6 +180,10 @@
qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
};
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+};
+
&mdss_dp_ctrl {
pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
index 33edebec2f72..f933586183ec 100644
--- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
@@ -181,6 +181,7 @@
"cfg_ahb_clk", "xo";
qcom,core-clk-rate = <133330000>;
+ qcom,core-clk-rate-hs = <66666667>;
resets = <&clock_gcc GCC_USB_30_BCR>;
reset-names = "core_reset";
@@ -192,6 +193,7 @@
interrupts = <0 131 0>;
usb-phy = <&qusb_phy0>, <&ssphy>;
tx-fifo-resize;
+ snps,usb3-u1u2-disable;
snps,nominal-elastic-buffer;
snps,disable-clk-gating;
snps,has-lpm-erratum;
@@ -375,6 +377,8 @@
0x8f8 0x77 0x00
0x4fc 0x80 0x00
0x8fc 0x80 0x00
+ 0x564 0x00 0x00
+ 0x964 0x00 0x00
0x4c0 0x0a 0x00
0x8c0 0x0a 0x00
0x504 0x03 0x00
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
index 7d293f8d821c..19862f02aa84 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
@@ -147,7 +147,13 @@
23 1e 07 08 05 03 04 a0
23 18 07 08 04 03 04 a0];
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
};
&dsi_dual_nt36850_truly_cmd {
@@ -195,7 +201,13 @@
20 12 05 06 03 13 04 a0];
qcom,config-select = <&dsi_nt35597_truly_dsc_cmd_config2>;
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
};
&dsi_dual_nt35597_video {
@@ -229,7 +241,13 @@
qcom,mdss-dsi-pan-enable-dynamic-fps;
qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
};
&dsi_nt35695b_truly_fhd_cmd {
@@ -239,7 +257,13 @@
24 1e 08 09 05 03 04 a0
24 1a 08 09 05 03 04 a0];
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
};
&dsi_truly_1080_vid {
@@ -253,7 +277,14 @@
qcom,mdss-dsi-pan-enable-dynamic-fps;
qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x1c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x1c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+
};
&dsi_truly_1080_cmd {
@@ -263,7 +294,14 @@
23 1e 08 09 05 03 04 a0
23 1a 08 09 05 03 04 a0];
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x1c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x1c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+
};
&dsi_rm67195_amoled_fhd_cmd {
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
index b263d2a68792..787c4f1e2fb6 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
@@ -505,7 +505,16 @@
qcom,msm_ext_disp = <&msm_ext_disp>;
- qcom,aux-cfg-settings = [00 13 00 00 0a 28 0a 03 b7 03];
+ qcom,aux-cfg0-settings = [20 00];
+ qcom,aux-cfg1-settings = [24 13 23 1d];
+ qcom,aux-cfg2-settings = [28 00];
+ qcom,aux-cfg3-settings = [2c 00];
+ qcom,aux-cfg4-settings = [30 0a];
+ qcom,aux-cfg5-settings = [34 28];
+ qcom,aux-cfg6-settings = [38 0a];
+ qcom,aux-cfg7-settings = [3c 03];
+ qcom,aux-cfg8-settings = [40 b7];
+ qcom,aux-cfg9-settings = [44 03];
qcom,logical2physical-lane-map = [00 01 02 03];
qcom,phy-register-offset = <0x4>;
qcom,max-pclk-frequency-khz = <300000>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
index ed3b3d89d392..cb554a639d71 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
@@ -170,6 +170,10 @@
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+};
+
&sdhc_1 {
/* device core power supply */
vdd-supply = <&pm660l_l4>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts
index 7ca31fcc41a2..c27f76d3027b 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts
@@ -25,13 +25,19 @@
qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
};
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>;
oledb-supply = <&pm660a_oledb>;
lab-supply = <&lab_regulator>;
ibb-supply = <&ibb_regulator>;
};
&mdss_dsi1 {
+ status = "disabled";
oledb-supply = <&pm660a_oledb>;
lab-supply = <&lab_regulator>;
ibb-supply = <&ibb_regulator>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts
index d6e1f6a32def..eb5e4999fb67 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts
@@ -25,13 +25,19 @@
qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
};
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>;
oledb-supply = <&pm660a_oledb>;
lab-supply = <&lab_regulator>;
ibb-supply = <&ibb_regulator>;
};
&mdss_dsi1 {
+ status = "disabled";
oledb-supply = <&pm660a_oledb>;
lab-supply = <&lab_regulator>;
ibb-supply = <&ibb_regulator>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
index a93efdc38f41..8b6bbac171f4 100644
--- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
@@ -28,7 +28,7 @@
rpm-regulator-smpa5 {
status = "okay";
pm660_s5: regulator-s5 {
- regulator-min-microvolt = <1350000>;
+ regulator-min-microvolt = <1224000>;
regulator-max-microvolt = <1350000>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi
index be200f8dd531..2e576a51677f 100644
--- a/arch/arm/boot/dts/qcom/sdm660.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660.dtsi
@@ -466,6 +466,7 @@
interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>;
qcom,ee = <0>;
qcom,channel = <0>;
+ qcom,reserved-chan = <511>;
#address-cells = <2>;
#size-cells = <0>;
interrupt-controller;
diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 4dfca8fc49b3..1bc61ece2589 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -856,6 +856,13 @@
compatible = "atmel,at91sam9260-usart";
reg = <0xf801c000 0x100>;
interrupts = <24 IRQ_TYPE_LEVEL_HIGH 7>;
+ dmas = <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(35))>,
+ <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(36))>;
+ dma-names = "tx", "rx";
clocks = <&uart0_clk>;
clock-names = "usart";
status = "disabled";
@@ -865,6 +872,13 @@
compatible = "atmel,at91sam9260-usart";
reg = <0xf8020000 0x100>;
interrupts = <25 IRQ_TYPE_LEVEL_HIGH 7>;
+ dmas = <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(37))>,
+ <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(38))>;
+ dma-names = "tx", "rx";
clocks = <&uart1_clk>;
clock-names = "usart";
status = "disabled";
@@ -874,6 +888,13 @@
compatible = "atmel,at91sam9260-usart";
reg = <0xf8024000 0x100>;
interrupts = <26 IRQ_TYPE_LEVEL_HIGH 7>;
+ dmas = <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(39))>,
+ <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(40))>;
+ dma-names = "tx", "rx";
clocks = <&uart2_clk>;
clock-names = "usart";
status = "disabled";
@@ -985,6 +1006,13 @@
compatible = "atmel,at91sam9260-usart";
reg = <0xfc008000 0x100>;
interrupts = <27 IRQ_TYPE_LEVEL_HIGH 7>;
+ dmas = <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(41))>,
+ <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(42))>;
+ dma-names = "tx", "rx";
clocks = <&uart3_clk>;
clock-names = "usart";
status = "disabled";
@@ -993,6 +1021,13 @@
uart4: serial@fc00c000 {
compatible = "atmel,at91sam9260-usart";
reg = <0xfc00c000 0x100>;
+ dmas = <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(43))>,
+ <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(44))>;
+ dma-names = "tx", "rx";
interrupts = <28 IRQ_TYPE_LEVEL_HIGH 7>;
clocks = <&uart4_clk>;
clock-names = "usart";
diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig
index 32cf48661c9b..a8844dafe592 100644
--- a/arch/arm/configs/sdm660-perf_defconfig
+++ b/arch/arm/configs/sdm660-perf_defconfig
@@ -359,6 +359,7 @@ CONFIG_REGULATOR_MSM_GFX_LDO=y
CONFIG_REGULATOR_RPM_SMD=y
CONFIG_REGULATOR_QPNP=y
CONFIG_REGULATOR_QPNP_LABIBB=y
+CONFIG_REGULATOR_QPNP_LCDB=y
CONFIG_REGULATOR_QPNP_OLEDB=y
CONFIG_REGULATOR_SPM=y
CONFIG_REGULATOR_CPR3_HMSS=y
diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig
index c4b0eabe2fbf..30a71904747f 100644
--- a/arch/arm/configs/sdm660_defconfig
+++ b/arch/arm/configs/sdm660_defconfig
@@ -635,6 +635,7 @@ CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_PAGEALLOC=y
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
CONFIG_SLUB_DEBUG_PANIC_ON=y
+CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y
CONFIG_DEBUG_OBJECTS=y
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c
index 679c589c4828..1f7b98e1a00d 100644
--- a/arch/arm/crypto/aes-ce-glue.c
+++ b/arch/arm/crypto/aes-ce-glue.c
@@ -369,7 +369,7 @@ static struct crypto_alg aes_algs[] = { {
.cra_blkcipher = {
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_BLOCK_SIZE,
+ .ivsize = 0,
.setkey = ce_aes_setkey,
.encrypt = ecb_encrypt,
.decrypt = ecb_decrypt,
@@ -446,7 +446,7 @@ static struct crypto_alg aes_algs[] = { {
.cra_ablkcipher = {
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_BLOCK_SIZE,
+ .ivsize = 0,
.setkey = ablk_set_key,
.encrypt = ablk_encrypt,
.decrypt = ablk_decrypt,
diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c
index 54a5aeab988d..bbbffe946122 100644
--- a/arch/arm/kernel/vdso.c
+++ b/arch/arm/kernel/vdso.c
@@ -17,6 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/cache.h>
#include <linux/elf.h>
#include <linux/err.h>
#include <linux/kernel.h>
@@ -39,7 +40,7 @@
static struct page **vdso_text_pagelist;
/* Total number of pages needed for the data and text portions of the VDSO. */
-unsigned int vdso_total_pages __read_mostly;
+unsigned int vdso_total_pages __ro_after_init;
/*
* The VDSO data page.
@@ -47,13 +48,13 @@ unsigned int vdso_total_pages __read_mostly;
static union vdso_data_store vdso_data_store __page_aligned_data;
static struct vdso_data *vdso_data = &vdso_data_store.data;
-static struct page *vdso_data_page;
-static struct vm_special_mapping vdso_data_mapping = {
+static struct page *vdso_data_page __ro_after_init;
+static const struct vm_special_mapping vdso_data_mapping = {
.name = "[vvar]",
.pages = &vdso_data_page,
};
-static struct vm_special_mapping vdso_text_mapping = {
+static struct vm_special_mapping vdso_text_mapping __ro_after_init = {
.name = "[vdso]",
};
@@ -67,7 +68,7 @@ struct elfinfo {
/* Cached result of boot-time check for whether the arch timer exists,
* and if so, whether the virtual counter is useable.
*/
-static bool cntvct_ok __read_mostly;
+static bool cntvct_ok __ro_after_init;
static bool __init cntvct_functional(void)
{
@@ -224,7 +225,7 @@ static int install_vvar(struct mm_struct *mm, unsigned long addr)
VM_READ | VM_MAYREAD,
&vdso_data_mapping);
- return IS_ERR(vma) ? PTR_ERR(vma) : 0;
+ return PTR_ERR_OR_ZERO(vma);
}
/* assumes mmap_sem is write-locked */
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 23726fb31741..d687f860a2da 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -286,6 +286,22 @@ static void at91_ddr_standby(void)
at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1);
}
+static void sama5d3_ddr_standby(void)
+{
+ u32 lpr0;
+ u32 saved_lpr0;
+
+ saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR);
+ lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
+ lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN;
+
+ at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0);
+
+ cpu_do_idle();
+
+ at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0);
+}
+
/* We manage both DDRAM/SDRAM controllers, we need more than one value to
* remember.
*/
@@ -320,7 +336,7 @@ static const struct of_device_id const ramc_ids[] __initconst = {
{ .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby },
{ .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby },
{ .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby },
- { .compatible = "atmel,sama5d3-ddramc", .data = at91_ddr_standby },
+ { .compatible = "atmel,sama5d3-ddramc", .data = sama5d3_ddr_standby },
{ /*sentinel*/ }
};
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index b58d8e0d9ed1..a25d6b0e22a4 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -67,7 +67,8 @@ __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot,
static void __dma_free_remap(void *cpu_addr, size_t size, bool no_warn);
-static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot);
+static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
+ bool coherent);
static void *arm_dma_remap(struct device *dev, void *cpu_addr,
dma_addr_t handle, size_t size,
@@ -76,6 +77,33 @@ static void *arm_dma_remap(struct device *dev, void *cpu_addr,
static void arm_dma_unremap(struct device *dev, void *remapped_addr,
size_t size);
+static bool is_dma_coherent(struct device *dev, struct dma_attrs *attrs)
+{
+ bool is_coherent;
+
+ if (dma_get_attr(DMA_ATTR_FORCE_COHERENT, attrs))
+ is_coherent = true;
+ else if (dma_get_attr(DMA_ATTR_FORCE_NON_COHERENT, attrs))
+ is_coherent = false;
+ else if (is_device_dma_coherent(dev))
+ is_coherent = true;
+ else
+ is_coherent = false;
+
+ return is_coherent;
+}
+
+static int __get_iommu_pgprot(struct dma_attrs *attrs, int prot,
+ bool coherent)
+{
+ if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs))
+ prot |= IOMMU_NOEXEC;
+ if (coherent)
+ prot |= IOMMU_CACHE;
+
+ return prot;
+}
+
/**
* arm_dma_map_page - map a portion of a page for streaming DMA
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
@@ -245,7 +273,7 @@ static u64 get_coherent_dma_mask(struct device *dev)
}
static void __dma_clear_buffer(struct page *page, size_t size,
- struct dma_attrs *attrs)
+ struct dma_attrs *attrs, bool is_coherent)
{
/*
* Ensure that the allocated pages are zeroed, and that any data
@@ -258,18 +286,22 @@ static void __dma_clear_buffer(struct page *page, size_t size,
void *ptr = kmap_atomic(page);
if (!dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs))
memset(ptr, 0, PAGE_SIZE);
- dmac_flush_range(ptr, ptr + PAGE_SIZE);
+ if (!is_coherent)
+ dmac_flush_range(ptr, ptr + PAGE_SIZE);
kunmap_atomic(ptr);
page++;
size -= PAGE_SIZE;
}
- outer_flush_range(base, end);
+ if (!is_coherent)
+ outer_flush_range(base, end);
} else {
void *ptr = page_address(page);
if (!dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs))
memset(ptr, 0, size);
- dmac_flush_range(ptr, ptr + size);
- outer_flush_range(__pa(ptr), __pa(ptr) + size);
+ if (!is_coherent) {
+ dmac_flush_range(ptr, ptr + size);
+ outer_flush_range(__pa(ptr), __pa(ptr) + size);
+ }
}
}
@@ -277,7 +309,8 @@ static void __dma_clear_buffer(struct page *page, size_t size,
* Allocate a DMA buffer for 'dev' of size 'size' using the
* specified gfp mask. Note that 'size' must be page aligned.
*/
-static struct page *__dma_alloc_buffer(struct device *dev, size_t size, gfp_t gfp)
+static struct page *__dma_alloc_buffer(struct device *dev, size_t size,
+ gfp_t gfp, bool coherent)
{
unsigned long order = get_order(size);
struct page *page, *p, *e;
@@ -293,7 +326,7 @@ static struct page *__dma_alloc_buffer(struct device *dev, size_t size, gfp_t gf
for (p = page + (size >> PAGE_SHIFT), e = page + (1 << order); p < e; p++)
__free_page(p);
- __dma_clear_buffer(page, size, NULL);
+ __dma_clear_buffer(page, size, NULL, coherent);
return page;
}
@@ -527,7 +560,7 @@ static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp,
{
struct page *page;
void *ptr = NULL;
- page = __dma_alloc_buffer(dev, size, gfp);
+ page = __dma_alloc_buffer(dev, size, gfp, false);
if (!page)
return NULL;
if (!want_vaddr)
@@ -602,7 +635,7 @@ static void *__alloc_from_contiguous(struct device *dev, size_t size,
*/
if (!(dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs) &&
dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs)))
- __dma_clear_buffer(page, size, attrs);
+ __dma_clear_buffer(page, size, attrs, false);
if (PageHighMem(page)) {
if (!want_vaddr) {
@@ -643,15 +676,13 @@ static void __free_from_contiguous(struct device *dev, struct page *page,
dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT);
}
-static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot)
+static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
+ bool coherent)
{
- if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
- prot = pgprot_writecombine(prot);
- else if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs))
+ if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs))
prot = pgprot_stronglyordered(prot);
- /* if non-consistent just pass back what was given */
- else if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
- prot = pgprot_dmacoherent(prot);
+ else if (!coherent || dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
+ prot = pgprot_writecombine(prot);
return prot;
}
@@ -673,10 +704,10 @@ static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot)
#endif /* CONFIG_MMU */
static void *__alloc_simple_buffer(struct device *dev, size_t size, gfp_t gfp,
- struct page **ret_page)
+ struct page **ret_page, bool coherent)
{
struct page *page;
- page = __dma_alloc_buffer(dev, size, gfp);
+ page = __dma_alloc_buffer(dev, size, gfp, coherent);
if (!page)
return NULL;
@@ -724,12 +755,14 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
want_vaddr = !dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs);
if (nommu())
- addr = __alloc_simple_buffer(dev, size, gfp, &page);
+ addr = __alloc_simple_buffer(dev, size, gfp, &page,
+ is_coherent);
else if (dev_get_cma_area(dev) && (gfp & __GFP_DIRECT_RECLAIM))
addr = __alloc_from_contiguous(dev, size, prot, &page,
caller, attrs);
else if (is_coherent)
- addr = __alloc_simple_buffer(dev, size, gfp, &page);
+ addr = __alloc_simple_buffer(dev, size, gfp, &page,
+ is_coherent);
else if (!gfpflags_allow_blocking(gfp))
addr = __alloc_from_pool(size, &page);
else
@@ -749,7 +782,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
gfp_t gfp, struct dma_attrs *attrs)
{
- pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
+ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, false);
return __dma_alloc(dev, size, handle, gfp, prot, false,
attrs, __builtin_return_address(0));
@@ -792,8 +825,9 @@ static void *arm_dma_remap(struct device *dev, void *cpu_addr,
struct dma_attrs *attrs)
{
void *ptr;
+ bool is_coherent = is_dma_coherent(dev, attrs);
struct page *page = pfn_to_page(dma_to_pfn(dev, handle));
- pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
+ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, is_coherent);
unsigned long offset = handle & ~PAGE_MASK;
size = PAGE_ALIGN(size + offset);
@@ -837,7 +871,8 @@ int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
struct dma_attrs *attrs)
{
#ifdef CONFIG_MMU
- vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot);
+ vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot,
+ false);
#endif /* CONFIG_MMU */
return __arm_dma_mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
}
@@ -1238,6 +1273,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
size_t count = size >> PAGE_SHIFT;
size_t array_size = count * sizeof(struct page *);
int i = 0;
+ bool is_coherent = is_dma_coherent(dev, attrs);
if (array_size <= PAGE_SIZE)
pages = kzalloc(array_size, GFP_KERNEL);
@@ -1255,7 +1291,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
if (!page)
goto error;
- __dma_clear_buffer(page, size, NULL);
+ __dma_clear_buffer(page, size, attrs, is_coherent);
for (i = 0; i < count; i++)
pages[i] = page + i;
@@ -1299,7 +1335,8 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
pages[i + j] = pages[i] + j;
}
- __dma_clear_buffer(pages[i], PAGE_SIZE << order, NULL);
+ __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs,
+ is_coherent);
i += 1 << order;
count -= 1 << order;
}
@@ -1353,16 +1390,20 @@ __iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot,
* Create a mapping in device IO address space for specified pages
*/
static dma_addr_t
-__iommu_create_mapping(struct device *dev, struct page **pages, size_t size)
+__iommu_create_mapping(struct device *dev, struct page **pages, size_t size,
+ struct dma_attrs *attrs)
{
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
dma_addr_t dma_addr, iova;
int i;
+ int prot = IOMMU_READ | IOMMU_WRITE;
dma_addr = __alloc_iova(mapping, size);
if (dma_addr == DMA_ERROR_CODE)
return dma_addr;
+ prot = __get_iommu_pgprot(attrs, prot,
+ is_dma_coherent(dev, attrs));
iova = dma_addr;
for (i = 0; i < count; ) {
@@ -1377,8 +1418,7 @@ __iommu_create_mapping(struct device *dev, struct page **pages, size_t size)
break;
len = (j - i) << PAGE_SHIFT;
- ret = iommu_map(mapping->domain, iova, phys, len,
- IOMMU_READ|IOMMU_WRITE);
+ ret = iommu_map(mapping->domain, iova, phys, len, prot);
if (ret < 0)
goto fail;
iova += len;
@@ -1435,23 +1475,52 @@ static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs)
}
static void *__iommu_alloc_atomic(struct device *dev, size_t size,
- dma_addr_t *handle)
+ dma_addr_t *handle, gfp_t gfp,
+ struct dma_attrs *attrs)
{
struct page *page;
+ struct page **pages;
+ size_t count = size >> PAGE_SHIFT;
+ size_t array_size = count * sizeof(struct page *);
void *addr;
+ int i;
+ bool coherent = is_dma_coherent(dev, attrs);
- addr = __alloc_from_pool(size, &page);
- if (!addr)
+ if (array_size <= PAGE_SIZE)
+ pages = kzalloc(array_size, gfp);
+ else
+ pages = vzalloc(array_size);
+
+ if (!pages)
return NULL;
- *handle = __iommu_create_mapping(dev, &page, size);
+ if (coherent) {
+ page = alloc_pages(gfp, get_order(size));
+ addr = page ? page_address(page) : NULL;
+ } else {
+ addr = __alloc_from_pool(size, &page);
+ }
+
+ if (!addr)
+ goto err_free;
+
+ for (i = 0; i < count ; i++)
+ pages[i] = page + i;
+
+ *handle = __iommu_create_mapping(dev, pages, size, attrs);
if (*handle == DMA_ERROR_CODE)
goto err_mapping;
+ kvfree(pages);
return addr;
err_mapping:
- __free_from_pool(addr, size);
+ if (coherent)
+ __free_pages(page, get_order(size));
+ else
+ __free_from_pool(addr, size);
+err_free:
+ kvfree(pages);
return NULL;
}
@@ -1465,7 +1534,9 @@ static void __iommu_free_atomic(struct device *dev, void *cpu_addr,
static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
{
- pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
+
+ bool coherent = is_dma_coherent(dev, attrs);
+ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, coherent);
struct page **pages;
void *addr = NULL;
@@ -1473,7 +1544,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
size = PAGE_ALIGN(size);
if (!gfpflags_allow_blocking(gfp))
- return __iommu_alloc_atomic(dev, size, handle);
+ return __iommu_alloc_atomic(dev, size, handle, gfp, attrs);
/*
* Following is a work-around (a.k.a. hack) to prevent pages
@@ -1488,7 +1559,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
if (!pages)
return NULL;
- *handle = __iommu_create_mapping(dev, pages, size);
+ *handle = __iommu_create_mapping(dev, pages, size, attrs);
if (*handle == DMA_ERROR_CODE)
goto err_buffer;
@@ -1518,8 +1589,10 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
struct page **pages = __iommu_get_pages(cpu_addr, attrs);
unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
unsigned long off = vma->vm_pgoff;
+ bool coherent = is_dma_coherent(dev, attrs);
- vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot);
+ vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot,
+ coherent);
if (!pages)
return -ENXIO;
@@ -1750,6 +1823,8 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg,
dev_err(dev, "Couldn't allocate iova for sg %p\n", sg);
return 0;
}
+ prot = __get_iommu_pgprot(attrs, prot,
+ is_dma_coherent(dev, attrs));
ret = iommu_map_sg(mapping->domain, iova, sg, nents, prot);
if (ret != total_length) {
@@ -1836,6 +1911,13 @@ void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
{
struct scatterlist *s;
int i;
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+ dma_addr_t iova = sg_dma_address(sg);
+ bool iova_coherent = iommu_is_iova_coherent(mapping->domain, iova);
+
+ if (iova_coherent)
+ return;
+
for_each_sg(sg, s, nents, i)
__dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir);
@@ -1854,6 +1936,12 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
{
struct scatterlist *s;
int i;
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+ dma_addr_t iova = sg_dma_address(sg);
+ bool iova_coherent = iommu_is_iova_coherent(mapping->domain, iova);
+
+ if (iova_coherent)
+ return;
for_each_sg(sg, s, nents, i)
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
@@ -1887,6 +1975,8 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p
return dma_addr;
prot = __dma_direction_to_prot(dir);
+ prot = __get_iommu_pgprot(attrs, prot,
+ is_dma_coherent(dev, attrs));
ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page) +
start_offset, len, prot);
@@ -1913,7 +2003,8 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
- if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+ if (!is_dma_coherent(dev, attrs) &&
+ !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
__dma_page_cpu_to_dev(page, offset, size, dir);
return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs);
@@ -1960,7 +2051,8 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
int offset = handle & ~PAGE_MASK;
int len = PAGE_ALIGN(size + offset);
- if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+ if (!(is_dma_coherent(dev, attrs) ||
+ dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)))
__dma_page_dev_to_cpu(page, offset, size, dir);
iommu_unmap(mapping->domain, iova, len);
@@ -1974,8 +2066,10 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev,
dma_addr_t iova = handle & PAGE_MASK;
struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova));
unsigned int offset = handle & ~PAGE_MASK;
+ bool iova_coherent = iommu_is_iova_coherent(mapping->domain, handle);
- __dma_page_dev_to_cpu(page, offset, size, dir);
+ if (!iova_coherent)
+ __dma_page_dev_to_cpu(page, offset, size, dir);
}
static void arm_iommu_sync_single_for_device(struct device *dev,
@@ -1985,10 +2079,31 @@ static void arm_iommu_sync_single_for_device(struct device *dev,
dma_addr_t iova = handle & PAGE_MASK;
struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova));
unsigned int offset = handle & ~PAGE_MASK;
+ bool iova_coherent = iommu_is_iova_coherent(mapping->domain, handle);
- __dma_page_cpu_to_dev(page, offset, size, dir);
+ if (!iova_coherent)
+ __dma_page_cpu_to_dev(page, offset, size, dir);
+}
+
+static int arm_iommu_dma_supported(struct device *dev, u64 mask)
+{
+ struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
+
+ if (!mapping) {
+ dev_warn(dev, "No IOMMU mapping for device\n");
+ return 0;
+ }
+
+ return iommu_dma_supported(mapping->domain, dev, mask);
+}
+
+static int arm_iommu_mapping_error(struct device *dev,
+ dma_addr_t dma_addr)
+{
+ return dma_addr == DMA_ERROR_CODE;
}
+
const struct dma_map_ops iommu_ops = {
.alloc = arm_iommu_alloc_attrs,
.free = arm_iommu_free_attrs,
@@ -2006,6 +2121,8 @@ const struct dma_map_ops iommu_ops = {
.sync_sg_for_device = arm_iommu_sync_sg_for_device,
.set_dma_mask = arm_dma_set_mask,
+ .dma_supported = arm_iommu_dma_supported,
+ .mapping_error = arm_iommu_mapping_error,
};
const struct dma_map_ops iommu_coherent_ops = {
@@ -2052,6 +2169,9 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size)
if (!bitmap_size)
return ERR_PTR(-EINVAL);
+ WARN(!IS_ALIGNED(size, SZ_128M),
+ "size is not aligned to 128M, alignment enforced");
+
if (bitmap_size > PAGE_SIZE) {
extensions = bitmap_size / PAGE_SIZE;
bitmap_size = PAGE_SIZE;
@@ -2074,7 +2194,7 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size)
mapping->nr_bitmaps = 1;
mapping->extensions = extensions;
mapping->base = base;
- mapping->bits = bits;
+ mapping->bits = BITS_PER_BYTE * bitmap_size;
spin_lock_init(&mapping->lock);
diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile
index 1160434eece0..59a8fa7b8a3b 100644
--- a/arch/arm/vdso/Makefile
+++ b/arch/arm/vdso/Makefile
@@ -74,5 +74,5 @@ $(MODLIB)/vdso: FORCE
@mkdir -p $(MODLIB)/vdso
PHONY += vdso_install
-vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso FORCE
+vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso
$(call cmd,vdso_install)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 3c89828b9af2..69f08519301d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1023,6 +1023,26 @@ config BUILD_ARM64_APPENDED_DTB_IMAGE
DTBs to be built by default (instead of a standalone Image.gz.)
The image will built in arch/arm64/boot/Image.gz-dtb
+choice
+ prompt "Appended DTB Kernel Image name"
+ depends on BUILD_ARM64_APPENDED_DTB_IMAGE
+ help
+ Enabling this option will cause a specific kernel image Image or
+ Image.gz to be used for final image creation.
+ The image will built in arch/arm64/boot/IMAGE-NAME-dtb
+
+ config IMG_GZ_DTB
+ bool "Image.gz-dtb"
+ config IMG_DTB
+ bool "Image-dtb"
+endchoice
+
+config BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME
+ string
+ depends on BUILD_ARM64_APPENDED_DTB_IMAGE
+ default "Image.gz-dtb" if IMG_GZ_DTB
+ default "Image-dtb" if IMG_DTB
+
config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES
string "Default dtb names"
depends on BUILD_ARM64_APPENDED_DTB_IMAGE
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index fcfa3c7dedc1..f656c52d7e10 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -85,7 +85,7 @@ core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
# Default target when executing plain make
ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y)
-KBUILD_IMAGE := Image.gz-dtb
+KBUILD_IMAGE := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME))
else
KBUILD_IMAGE := Image.gz
endif
@@ -132,6 +132,16 @@ archclean:
$(Q)$(MAKE) $(clean)=$(boot)
$(Q)$(MAKE) $(clean)=$(boot)/dts
+# We need to generate vdso-offsets.h before compiling certain files in kernel/.
+# In order to do that, we should use the archprepare target, but we can't since
+# asm-offsets.h is included in some files used to generate vdso-offsets.h, and
+# asm-offsets.h is built in prepare0, for which archprepare is a dependency.
+# Therefore we need to generate the header after prepare0 has been made, hence
+# this hack.
+prepare: vdso_prepare
+vdso_prepare: prepare0
+ $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso include/generated/vdso-offsets.h
+
define archhelp
echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)'
echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
diff --git a/arch/arm64/boot/dts/arm/juno-r1.dts b/arch/arm64/boot/dts/arm/juno-r1.dts
index 93bc3d7d51c0..29315af22147 100644
--- a/arch/arm64/boot/dts/arm/juno-r1.dts
+++ b/arch/arm64/boot/dts/arm/juno-r1.dts
@@ -60,6 +60,28 @@
};
};
+ idle-states {
+ entry-method = "arm,psci";
+
+ CPU_SLEEP_0: cpu-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ local-timer-stop;
+ entry-latency-us = <300>;
+ exit-latency-us = <1200>;
+ min-residency-us = <2000>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x1010000>;
+ local-timer-stop;
+ entry-latency-us = <400>;
+ exit-latency-us = <1200>;
+ min-residency-us = <2500>;
+ };
+ };
+
A57_0: cpu@0 {
compatible = "arm,cortex-a57","arm,armv8";
reg = <0x0 0x0>;
@@ -67,6 +89,7 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A57_1: cpu@1 {
@@ -76,6 +99,7 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A53_0: cpu@100 {
@@ -85,6 +109,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A53_1: cpu@101 {
@@ -94,6 +119,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A53_2: cpu@102 {
@@ -103,6 +129,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A53_3: cpu@103 {
@@ -112,6 +139,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A57_L2: l2-cache0 {
diff --git a/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi b/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi
new file mode 100644
index 000000000000..38207e4391ab
--- /dev/null
+++ b/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi
@@ -0,0 +1,147 @@
+/*
+ * ARM JUNO specific energy cost model data. There are no unit requirements for
+ * the data. Data can be normalized to any reference point, but the
+ * normalization must be consistent. That is, one bogo-joule/watt must be the
+ * same quantity for all data, but we don't care what it is.
+ */
+
+/* static struct idle_state idle_states_cluster_a53[] = { */
+/* { .power = 56 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */
+/* { .power = 56 }, /\* WFI *\/ */
+/* { .power = 56 }, /\* cpu-sleep-0 *\/ */
+/* { .power = 17 }, /\* cluster-sleep-0 *\/ */
+/* }; */
+
+/* static struct idle_state idle_states_cluster_a57[] = { */
+/* { .power = 65 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */
+/* { .power = 65 }, /\* WFI *\/ */
+/* { .power = 65 }, /\* cpu-sleep-0 *\/ */
+/* { .power = 24 }, /\* cluster-sleep-0 *\/ */
+/* }; */
+
+/* static struct capacity_state cap_states_cluster_a53[] = { */
+/* /\* Power per cluster *\/ */
+/* { .cap = 235, .power = 26, }, /\* 450 MHz *\/ */
+/* { .cap = 303, .power = 30, }, /\* 575 MHz *\/ */
+/* { .cap = 368, .power = 39, }, /\* 700 MHz *\/ */
+/* { .cap = 406, .power = 47, }, /\* 775 MHz *\/ */
+/* { .cap = 447, .power = 57, }, /\* 850 Mhz *\/ */
+/* }; */
+
+/* static struct capacity_state cap_states_cluster_a57[] = { */
+/* /\* Power per cluster *\/ */
+/* { .cap = 417, .power = 24, }, /\* 450 MHz *\/ */
+/* { .cap = 579, .power = 32, }, /\* 625 MHz *\/ */
+/* { .cap = 744, .power = 43, }, /\* 800 MHz *\/ */
+/* { .cap = 883, .power = 49, }, /\* 950 MHz *\/ */
+/* { .cap = 1024, .power = 64, }, /\* 1100 MHz *\/ */
+/* }; */
+
+/* static struct sched_group_energy energy_cluster_a53 = { */
+/* .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a53), */
+/* .idle_states = idle_states_cluster_a53, */
+/* .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a53), */
+/* .cap_states = cap_states_cluster_a53, */
+/* }; */
+
+/* static struct sched_group_energy energy_cluster_a57 = { */
+/* .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a57), */
+/* .idle_states = idle_states_cluster_a57, */
+/* .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a57), */
+/* .cap_states = cap_states_cluster_a57, */
+/* }; */
+
+/* static struct idle_state idle_states_core_a53[] = { */
+/* { .power = 6 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */
+/* { .power = 6 }, /\* WFI *\/ */
+/* { .power = 0 }, /\* cpu-sleep-0 *\/ */
+/* { .power = 0 }, /\* cluster-sleep-0 *\/ */
+/* }; */
+
+/* static struct idle_state idle_states_core_a57[] = { */
+/* { .power = 15 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */
+/* { .power = 15 }, /\* WFI *\/ */
+/* { .power = 0 }, /\* cpu-sleep-0 *\/ */
+/* { .power = 0 }, /\* cluster-sleep-0 *\/ */
+/* }; */
+
+/* static struct capacity_state cap_states_core_a53[] = { */
+/* /\* Power per cpu *\/ */
+/* { .cap = 235, .power = 33, }, /\* 450 MHz *\/ */
+/* { .cap = 302, .power = 46, }, /\* 575 MHz *\/ */
+/* { .cap = 368, .power = 61, }, /\* 700 MHz *\/ */
+/* { .cap = 406, .power = 76, }, /\* 775 MHz *\/ */
+/* { .cap = 447, .power = 93, }, /\* 850 Mhz *\/ */
+/* }; */
+
+/* static struct capacity_state cap_states_core_a57[] = { */
+/* /\* Power per cpu *\/ */
+/* { .cap = 417, .power = 168, }, /\* 450 MHz *\/ */
+/* { .cap = 579, .power = 251, }, /\* 625 MHz *\/ */
+/* { .cap = 744, .power = 359, }, /\* 800 MHz *\/ */
+/* { .cap = 883, .power = 479, }, /\* 950 MHz *\/ */
+/* { .cap = 1024, .power = 616, }, /\* 1100 MHz *\/ */
+/* }; */
+
+energy-costs {
+ CPU_COST_A57: core-cost0 {
+ busy-cost-data = <
+ 417 168
+ 579 251
+ 744 359
+ 883 479
+ 1023 616
+ >;
+ idle-cost-data = <
+ 15
+ 15
+ 0
+ 0
+ >;
+ };
+ CPU_COST_A53: core-cost1 {
+ busy-cost-data = <
+ 235 33
+ 302 46
+ 368 61
+ 406 76
+ 447 93
+ >;
+ idle-cost-data = <
+ 6
+ 6
+ 0
+ 0
+ >;
+ };
+ CLUSTER_COST_A57: cluster-cost0 {
+ busy-cost-data = <
+ 417 24
+ 579 32
+ 744 43
+ 883 49
+ 1024 64
+ >;
+ idle-cost-data = <
+ 65
+ 65
+ 65
+ 24
+ >;
+ };
+ CLUSTER_COST_A53: cluster-cost1 {
+ busy-cost-data = <
+ 235 26
+ 303 30
+ 368 39
+ 406 47
+ 447 57
+ >;
+ idle-cost-data = <
+ 56
+ 56
+ 56
+ 17
+ >;
+ };
+};
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index 3e1a84b01b50..68816f71fa51 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -60,6 +60,28 @@
};
};
+ idle-states {
+ entry-method = "arm,psci";
+
+ CPU_SLEEP_0: cpu-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ local-timer-stop;
+ entry-latency-us = <300>;
+ exit-latency-us = <1200>;
+ min-residency-us = <2000>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x1010000>;
+ local-timer-stop;
+ entry-latency-us = <400>;
+ exit-latency-us = <1200>;
+ min-residency-us = <2500>;
+ };
+ };
+
A57_0: cpu@0 {
compatible = "arm,cortex-a57","arm,armv8";
reg = <0x0 0x0>;
@@ -67,6 +89,8 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ sched-energy-costs = <&CPU_COST_A57 &CLUSTER_COST_A57>;
};
A57_1: cpu@1 {
@@ -76,6 +100,8 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ sched-energy-costs = <&CPU_COST_A57 &CLUSTER_COST_A57>;
};
A53_0: cpu@100 {
@@ -85,6 +111,8 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>;
};
A53_1: cpu@101 {
@@ -94,6 +122,8 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>;
};
A53_2: cpu@102 {
@@ -103,6 +133,8 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>;
};
A53_3: cpu@103 {
@@ -112,6 +144,8 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>;
};
A57_L2: l2-cache0 {
@@ -121,6 +155,8 @@
A53_L2: l2-cache1 {
compatible = "cache";
};
+
+ /include/ "juno-sched-energy.dtsi"
};
pmu_a57 {
diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig
index 0031c55360c7..a6413f1f8b06 100644
--- a/arch/arm64/configs/msm-perf_defconfig
+++ b/arch/arm64/configs/msm-perf_defconfig
@@ -5,6 +5,9 @@ CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
@@ -236,7 +239,7 @@ CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -531,6 +534,7 @@ CONFIG_MSM_PIL_MSS_QDSP6V5=y
CONFIG_TRACER_PKT=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_AVTIMER=y
+CONFIG_MSM_QBT1000=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig
index 261a49d7944a..127a9d14128f 100644
--- a/arch/arm64/configs/msm_defconfig
+++ b/arch/arm64/configs/msm_defconfig
@@ -4,6 +4,9 @@ CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
@@ -228,7 +231,7 @@ CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -535,6 +538,7 @@ CONFIG_MSM_MPM_OF=y
CONFIG_MSM_AVTIMER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
+CONFIG_MSM_QBT1000=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 4f55414454fc..86b114354a2e 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -5,8 +5,13 @@ CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
@@ -186,6 +191,7 @@ CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
@@ -240,7 +246,7 @@ CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
@@ -565,6 +571,7 @@ CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
+CONFIG_MSM_QBT1000=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
@@ -588,7 +595,6 @@ CONFIG_PWM_QPNP=y
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_MSM_TZ_LOG=y
CONFIG_SENSORS_SSC=y
CONFIG_EXT2_FS=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index dc50257f5b7a..25b6f6eeb638 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -4,8 +4,13 @@ CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
@@ -82,6 +87,7 @@ CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_BOOST=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -185,6 +191,7 @@ CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
@@ -242,7 +249,7 @@ CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
@@ -585,6 +592,7 @@ CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
+CONFIG_MSM_QBT1000=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
@@ -609,7 +617,6 @@ CONFIG_ARM_GIC_V3_ACL=y
CONFIG_PHY_XGENE=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_MSM_TZ_LOG=y
CONFIG_SENSORS_SSC=y
CONFIG_EXT2_FS=y
@@ -638,6 +645,8 @@ CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
CONFIG_SLUB_DEBUG_PANIC_ON=y
+CONFIG_PAGE_POISONING=y
+CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y
CONFIG_DEBUG_OBJECTS=y
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig
index ccd653eaec7d..3322f8fa11fc 100644
--- a/arch/arm64/configs/msmcortex_mediabox_defconfig
+++ b/arch/arm64/configs/msmcortex_mediabox_defconfig
@@ -194,6 +194,7 @@ CONFIG_L2TP_V3=y
CONFIG_L2TP_IP=y
CONFIG_L2TP_ETH=y
CONFIG_BRIDGE=y
+CONFIG_VLAN_8021Q=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
@@ -543,7 +544,6 @@ CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_IRQ_HELPER=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_ICNSS=y
-CONFIG_ICNSS_DEBUG=y
CONFIG_MSM_GLADIATOR_ERP_V2=y
CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig
index ffb983587c31..9a4841db89be 100644
--- a/arch/arm64/configs/sdm660-perf_defconfig
+++ b/arch/arm64/configs/sdm660-perf_defconfig
@@ -526,6 +526,7 @@ CONFIG_ARM_SMMU=y
CONFIG_IOMMU_DEBUG=y
CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
+CONFIG_QCOM_COMMON_LOG=y
CONFIG_MSM_SMEM=y
CONFIG_QPNP_HAPTIC=y
CONFIG_QPNP_PBS=y
@@ -556,6 +557,7 @@ CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_ICNSS=y
CONFIG_MSM_RUN_QUEUE_STATS=y
CONFIG_MSM_BOOT_STATS=y
+CONFIG_QCOM_CPUSS_DUMP=y
CONFIG_MSM_ADSP_LOADER=y
CONFIG_MSM_CDSP_LOADER=y
CONFIG_MSM_PERFORMANCE=y
diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig
index bba52749284a..13ae21bdd562 100644
--- a/arch/arm64/configs/sdm660_defconfig
+++ b/arch/arm64/configs/sdm660_defconfig
@@ -645,6 +645,8 @@ CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
CONFIG_SLUB_DEBUG_PANIC_ON=y
+CONFIG_PAGE_POISONING=y
+CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y
CONFIG_DEBUG_OBJECTS=y
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig
index 2cf32e9887e1..de1aab4b5da8 100644
--- a/arch/arm64/crypto/Kconfig
+++ b/arch/arm64/crypto/Kconfig
@@ -23,6 +23,11 @@ config CRYPTO_GHASH_ARM64_CE
depends on ARM64 && KERNEL_MODE_NEON
select CRYPTO_HASH
+config CRYPTO_POLY_HASH_ARM64_CE
+ tristate "poly_hash (for HEH encryption mode) using ARMv8 Crypto Extensions"
+ depends on ARM64 && KERNEL_MODE_NEON
+ select CRYPTO_HASH
+
config CRYPTO_AES_ARM64_CE
tristate "AES core cipher using ARMv8 Crypto Extensions"
depends on ARM64 && KERNEL_MODE_NEON
diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile
index abb79b3cfcfe..f0a8f2475ea3 100644
--- a/arch/arm64/crypto/Makefile
+++ b/arch/arm64/crypto/Makefile
@@ -17,6 +17,9 @@ sha2-ce-y := sha2-ce-glue.o sha2-ce-core.o
obj-$(CONFIG_CRYPTO_GHASH_ARM64_CE) += ghash-ce.o
ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o
+obj-$(CONFIG_CRYPTO_POLY_HASH_ARM64_CE) += poly-hash-ce.o
+poly-hash-ce-y := poly-hash-ce-glue.o poly-hash-ce-core.o
+
obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o
CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto
diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c
index 6a51dfccfe71..448b874a4826 100644
--- a/arch/arm64/crypto/aes-glue.c
+++ b/arch/arm64/crypto/aes-glue.c
@@ -294,7 +294,7 @@ static struct crypto_alg aes_algs[] = { {
.cra_blkcipher = {
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_BLOCK_SIZE,
+ .ivsize = 0,
.setkey = aes_setkey,
.encrypt = ecb_encrypt,
.decrypt = ecb_decrypt,
@@ -371,7 +371,7 @@ static struct crypto_alg aes_algs[] = { {
.cra_ablkcipher = {
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_BLOCK_SIZE,
+ .ivsize = 0,
.setkey = ablk_set_key,
.encrypt = ablk_encrypt,
.decrypt = ablk_decrypt,
diff --git a/arch/arm64/crypto/poly-hash-ce-core.S b/arch/arm64/crypto/poly-hash-ce-core.S
new file mode 100644
index 000000000000..8ccb544c5526
--- /dev/null
+++ b/arch/arm64/crypto/poly-hash-ce-core.S
@@ -0,0 +1,163 @@
+/*
+ * Accelerated poly_hash implementation with ARMv8 PMULL instructions.
+ *
+ * Based on ghash-ce-core.S.
+ *
+ * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ * Copyright (C) 2017 Google, Inc. <ebiggers@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+ KEY .req v0
+ KEY2 .req v1
+ T1 .req v2
+ T2 .req v3
+ GSTAR .req v4
+ XL .req v5
+ XM .req v6
+ XH .req v7
+
+ .text
+ .arch armv8-a+crypto
+
+ /* 16-byte aligned (2**4 = 16); not required, but might as well */
+ .align 4
+.Lgstar:
+ .quad 0x87, 0x87
+
+/*
+ * void pmull_poly_hash_update(le128 *digest, const le128 *key,
+ * const u8 *src, unsigned int blocks,
+ * unsigned int partial);
+ */
+ENTRY(pmull_poly_hash_update)
+
+ /* Load digest into XL */
+ ld1 {XL.16b}, [x0]
+
+ /* Load key into KEY */
+ ld1 {KEY.16b}, [x1]
+
+ /* Load g*(x) = g(x) + x^128 = x^7 + x^2 + x + 1 into both halves of
+ * GSTAR */
+ adr x1, .Lgstar
+ ld1 {GSTAR.2d}, [x1]
+
+ /* Set KEY2 to (KEY[1]+KEY[0]):(KEY[1]+KEY[0]). This is needed for
+ * Karatsuba multiplication. */
+ ext KEY2.16b, KEY.16b, KEY.16b, #8
+ eor KEY2.16b, KEY2.16b, KEY.16b
+
+ /* If 'partial' is nonzero, then we're finishing a pending block and
+ * should go right to the multiplication. */
+ cbnz w4, 1f
+
+0:
+ /* Add the next block from 'src' to the digest */
+ ld1 {T1.16b}, [x2], #16
+ eor XL.16b, XL.16b, T1.16b
+ sub w3, w3, #1
+
+1:
+ /*
+ * Multiply the current 128-bit digest (a1:a0, in XL) by the 128-bit key
+ * (b1:b0, in KEY) using Karatsuba multiplication.
+ */
+
+ /* T1 = (a1+a0):(a1+a0) */
+ ext T1.16b, XL.16b, XL.16b, #8
+ eor T1.16b, T1.16b, XL.16b
+
+ /* XH = a1 * b1 */
+ pmull2 XH.1q, XL.2d, KEY.2d
+
+ /* XL = a0 * b0 */
+ pmull XL.1q, XL.1d, KEY.1d
+
+ /* XM = (a1+a0) * (b1+b0) */
+ pmull XM.1q, T1.1d, KEY2.1d
+
+ /* XM += (XH[0]:XL[1]) + XL + XH */
+ ext T1.16b, XL.16b, XH.16b, #8
+ eor T2.16b, XL.16b, XH.16b
+ eor XM.16b, XM.16b, T1.16b
+ eor XM.16b, XM.16b, T2.16b
+
+ /*
+ * Now the 256-bit product is in XH[1]:XM:XL[0]. It represents a
+ * polynomial over GF(2) with degree as large as 255. We need to
+ * compute its remainder modulo g(x) = x^128+x^7+x^2+x+1. For this it
+ * is sufficient to compute the remainder of the high half 'c(x)x^128'
+ * add it to the low half. To reduce the high half we use the Barrett
+ * reduction method. The basic idea is that we can express the
+ * remainder p(x) as g(x)q(x) mod x^128, where q(x) = (c(x)x^128)/g(x).
+ * As detailed in [1], to avoid having to divide by g(x) at runtime the
+ * following equivalent expression can be derived:
+ *
+ * p(x) = [ g*(x)((c(x)q+(x))/x^128) ] mod x^128
+ *
+ * where g*(x) = x^128+g(x) = x^7+x^2+x+1, and q+(x) = x^256/g(x) = g(x)
+ * in this case. This is also equivalent to:
+ *
+ * p(x) = [ g*(x)((c(x)(x^128 + g*(x)))/x^128) ] mod x^128
+ * = [ g*(x)(c(x) + (c(x)g*(x))/x^128) ] mod x^128
+ *
+ * Since deg g*(x) < 64:
+ *
+ * p(x) = [ g*(x)(c(x) + ((c(x)/x^64)g*(x))/x^64) ] mod x^128
+ * = [ g*(x)((c(x)/x^64)x^64 + (c(x) mod x^64) +
+ * ((c(x)/x^64)g*(x))/x^64) ] mod x^128
+ *
+ * Letting t(x) = g*(x)(c(x)/x^64):
+ *
+ * p(x) = [ t(x)x^64 + g*(x)((c(x) mod x^64) + t(x)/x^64) ] mod x^128
+ *
+ * Therefore, to do the reduction we only need to issue two 64-bit =>
+ * 128-bit carryless multiplications: g*(x) times c(x)/x^64, and g*(x)
+ * times ((c(x) mod x^64) + t(x)/x^64). (Multiplication by x^64 doesn't
+ * count since it is simply a shift or move.)
+ *
+ * An alternate reduction method, also based on Barrett reduction and
+ * described in [1], uses only shifts and XORs --- no multiplications.
+ * However, the method with multiplications requires fewer instructions
+ * and is faster on processors with fast carryless multiplication.
+ *
+ * [1] "Intel Carry-Less Multiplication Instruction and its Usage for
+ * Computing the GCM Mode",
+ * https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf
+ */
+
+ /* 256-bit product is XH[1]:XM:XL[0], so c(x) is XH[1]:XM[1] */
+
+ /* T1 = t(x) = g*(x)(c(x)/x^64) */
+ pmull2 T1.1q, GSTAR.2d, XH.2d
+
+ /* T2 = g*(x)((c(x) mod x^64) + t(x)/x^64) */
+ eor T2.16b, XM.16b, T1.16b
+ pmull2 T2.1q, GSTAR.2d, T2.2d
+
+ /* Make XL[0] be the low half of the 128-bit result by adding the low 64
+ * bits of the T2 term to what was already there. The 't(x)x^64' term
+ * makes no difference, so skip it. */
+ eor XL.16b, XL.16b, T2.16b
+
+ /* Make XL[1] be the high half of the 128-bit result by adding the high
+ * 64 bits of the 't(x)x^64' and T2 terms to what was already in XM[0],
+ * then moving XM[0] to XL[1]. */
+ eor XM.16b, XM.16b, T1.16b
+ ext T2.16b, T2.16b, T2.16b, #8
+ eor XM.16b, XM.16b, T2.16b
+ mov XL.d[1], XM.d[0]
+
+ /* If more blocks remain, then loop back to process the next block;
+ * else, store the digest and return. */
+ cbnz w3, 0b
+ st1 {XL.16b}, [x0]
+ ret
+ENDPROC(pmull_poly_hash_update)
diff --git a/arch/arm64/crypto/poly-hash-ce-glue.c b/arch/arm64/crypto/poly-hash-ce-glue.c
new file mode 100644
index 000000000000..e195740c9ecf
--- /dev/null
+++ b/arch/arm64/crypto/poly-hash-ce-glue.c
@@ -0,0 +1,166 @@
+/*
+ * Accelerated poly_hash implementation with ARMv8 PMULL instructions.
+ *
+ * Based on ghash-ce-glue.c.
+ *
+ * poly_hash is part of the HEH (Hash-Encrypt-Hash) encryption mode, proposed in
+ * Internet Draft https://tools.ietf.org/html/draft-cope-heh-01.
+ *
+ * poly_hash is very similar to GHASH: both algorithms are keyed hashes which
+ * interpret their input data as coefficients of a polynomial over GF(2^128),
+ * then calculate a hash value by evaluating that polynomial at the point given
+ * by the key, e.g. using Horner's rule. The difference is that poly_hash uses
+ * the more natural "ble" convention to represent GF(2^128) elements, whereas
+ * GHASH uses the less natural "lle" convention (see include/crypto/gf128mul.h).
+ * The ble convention makes it simpler to implement GF(2^128) multiplication.
+ *
+ * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ * Copyright (C) 2017 Google Inc. <ebiggers@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <asm/neon.h>
+#include <crypto/b128ops.h>
+#include <crypto/internal/hash.h>
+#include <linux/cpufeature.h>
+#include <linux/crypto.h>
+#include <linux/module.h>
+
+/*
+ * Note: in this algorithm we currently use 'le128' to represent GF(2^128)
+ * elements, even though poly_hash-generic uses 'be128'. Both types are
+ * actually "wrong" because the elements are actually in 'ble' format, and there
+ * should be a ble type to represent this --- as well as lle, bbe, and lbe types
+ * for the other conventions for representing GF(2^128) elements. But
+ * practically it doesn't matter which type we choose here, so we just use le128
+ * since it's arguably more accurate, while poly_hash-generic still has to use
+ * be128 because the generic GF(2^128) multiplication functions all take be128.
+ */
+
+struct poly_hash_desc_ctx {
+ le128 digest;
+ unsigned int count;
+};
+
+asmlinkage void pmull_poly_hash_update(le128 *digest, const le128 *key,
+ const u8 *src, unsigned int blocks,
+ unsigned int partial);
+
+static int poly_hash_setkey(struct crypto_shash *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ if (keylen != sizeof(le128)) {
+ crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ memcpy(crypto_shash_ctx(tfm), key, sizeof(le128));
+ return 0;
+}
+
+static int poly_hash_init(struct shash_desc *desc)
+{
+ struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
+
+ ctx->digest = (le128) { 0 };
+ ctx->count = 0;
+ return 0;
+}
+
+static int poly_hash_update(struct shash_desc *desc, const u8 *src,
+ unsigned int len)
+{
+ struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
+ unsigned int partial = ctx->count % sizeof(le128);
+ u8 *dst = (u8 *)&ctx->digest + partial;
+
+ ctx->count += len;
+
+ /* Finishing at least one block? */
+ if (partial + len >= sizeof(le128)) {
+ const le128 *key = crypto_shash_ctx(desc->tfm);
+
+ if (partial) {
+ /* Finish the pending block. */
+ unsigned int n = sizeof(le128) - partial;
+
+ len -= n;
+ do {
+ *dst++ ^= *src++;
+ } while (--n);
+ }
+
+ /*
+ * Do the real work. If 'partial' is nonzero, this starts by
+ * multiplying 'digest' by 'key'. Then for each additional full
+ * block it adds the block to 'digest' and multiplies by 'key'.
+ */
+ kernel_neon_begin_partial(8);
+ pmull_poly_hash_update(&ctx->digest, key, src,
+ len / sizeof(le128), partial);
+ kernel_neon_end();
+
+ src += len - (len % sizeof(le128));
+ len %= sizeof(le128);
+ dst = (u8 *)&ctx->digest;
+ }
+
+ /* Continue adding the next block to 'digest'. */
+ while (len--)
+ *dst++ ^= *src++;
+ return 0;
+}
+
+static int poly_hash_final(struct shash_desc *desc, u8 *out)
+{
+ struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
+ unsigned int partial = ctx->count % sizeof(le128);
+
+ /* Finish the last block if needed. */
+ if (partial) {
+ const le128 *key = crypto_shash_ctx(desc->tfm);
+
+ kernel_neon_begin_partial(8);
+ pmull_poly_hash_update(&ctx->digest, key, NULL, 0, partial);
+ kernel_neon_end();
+ }
+
+ memcpy(out, &ctx->digest, sizeof(le128));
+ return 0;
+}
+
+static struct shash_alg poly_hash_alg = {
+ .digestsize = sizeof(le128),
+ .init = poly_hash_init,
+ .update = poly_hash_update,
+ .final = poly_hash_final,
+ .setkey = poly_hash_setkey,
+ .descsize = sizeof(struct poly_hash_desc_ctx),
+ .base = {
+ .cra_name = "poly_hash",
+ .cra_driver_name = "poly_hash-ce",
+ .cra_priority = 300,
+ .cra_ctxsize = sizeof(le128),
+ .cra_module = THIS_MODULE,
+ },
+};
+
+static int __init poly_hash_ce_mod_init(void)
+{
+ return crypto_register_shash(&poly_hash_alg);
+}
+
+static void __exit poly_hash_ce_mod_exit(void)
+{
+ crypto_unregister_shash(&poly_hash_alg);
+}
+
+MODULE_DESCRIPTION("Polynomial evaluation hash using ARMv8 Crypto Extensions");
+MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
+MODULE_LICENSE("GPL v2");
+
+module_cpu_feature_match(PMULL, poly_hash_ce_mod_init);
+module_exit(poly_hash_ce_mod_exit);
diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h
index 5082b30bc2c0..f9359d32fae5 100644
--- a/arch/arm64/include/asm/cache.h
+++ b/arch/arm64/include/asm/cache.h
@@ -18,17 +18,17 @@
#include <asm/cachetype.h>
-#define L1_CACHE_SHIFT 7
+#define L1_CACHE_SHIFT 6
#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
/*
* Memory returned by kmalloc() may be used for DMA, so we must make
- * sure that all such allocations are cache aligned. Otherwise,
- * unrelated code may cause parts of the buffer to be read into the
- * cache before the transfer is done, causing old data to be seen by
- * the CPU.
+ * sure that all such allocations are aligned to the maximum *known*
+ * cache line size on ARMv8 systems. Otherwise, unrelated code may
+ * cause parts of the buffer to be read into the cache before the
+ * transfer is done, causing old data to be seen by the CPU.
*/
-#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN (128)
#ifndef __ASSEMBLY__
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
index 9732908bfc8a..c72b8e201ab4 100644
--- a/arch/arm64/include/asm/hw_breakpoint.h
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -68,7 +68,11 @@ static inline void decode_ctrl_reg(u32 reg,
/* Lengths */
#define ARM_BREAKPOINT_LEN_1 0x1
#define ARM_BREAKPOINT_LEN_2 0x3
+#define ARM_BREAKPOINT_LEN_3 0x7
#define ARM_BREAKPOINT_LEN_4 0xf
+#define ARM_BREAKPOINT_LEN_5 0x1f
+#define ARM_BREAKPOINT_LEN_6 0x3f
+#define ARM_BREAKPOINT_LEN_7 0x7f
#define ARM_BREAKPOINT_LEN_8 0xff
/* Kernel stepping */
@@ -110,7 +114,7 @@ struct perf_event;
struct pmu;
extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
- int *gen_len, int *gen_type);
+ int *gen_len, int *gen_type, int *offset);
extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
diff --git a/arch/arm64/include/asm/vdso_datapage.h b/arch/arm64/include/asm/vdso_datapage.h
index de66199673d7..2b9a63771eda 100644
--- a/arch/arm64/include/asm/vdso_datapage.h
+++ b/arch/arm64/include/asm/vdso_datapage.h
@@ -22,6 +22,8 @@
struct vdso_data {
__u64 cs_cycle_last; /* Timebase at clocksource init */
+ __u64 raw_time_sec; /* Raw time */
+ __u64 raw_time_nsec;
__u64 xtime_clock_sec; /* Kernel time */
__u64 xtime_clock_nsec;
__u64 xtime_coarse_sec; /* Coarse time */
@@ -29,8 +31,10 @@ struct vdso_data {
__u64 wtm_clock_sec; /* Wall to monotonic time */
__u64 wtm_clock_nsec;
__u32 tb_seq_count; /* Timebase sequence counter */
- __u32 cs_mult; /* Clocksource multiplier */
- __u32 cs_shift; /* Clocksource shift */
+ /* cs_* members must be adjacent and in this order (ldp accesses) */
+ __u32 cs_mono_mult; /* NTP-adjusted clocksource multiplier */
+ __u32 cs_shift; /* Clocksource shift (mono = raw) */
+ __u32 cs_raw_mult; /* Raw clocksource multiplier */
__u32 tz_minuteswest; /* Whacky timezone stuff */
__u32 tz_dsttime;
__u32 use_syscall;
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index d3cfa681654f..69c203d4d63f 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -52,7 +52,3 @@ obj-y += $(arm64-obj-y) vdso/ probes/
obj-m += $(arm64-obj-m)
head-y := head.o
extra-y += $(head-y) vmlinux.lds
-
-# vDSO - this must be built first to generate the symbol offsets
-$(call objectify,$(arm64-obj-y)): $(obj)/vdso/vdso-offsets.h
-$(obj)/vdso/vdso-offsets.h: $(obj)/vdso
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index c9ea87198789..350c0e99fc6b 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -92,6 +92,7 @@ int main(void)
BLANK();
DEFINE(CLOCK_REALTIME, CLOCK_REALTIME);
DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC);
+ DEFINE(CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_RAW);
DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE);
DEFINE(CLOCK_MONOTONIC_COARSE,CLOCK_MONOTONIC_COARSE);
@@ -99,6 +100,8 @@ int main(void)
DEFINE(NSEC_PER_SEC, NSEC_PER_SEC);
BLANK();
DEFINE(VDSO_CS_CYCLE_LAST, offsetof(struct vdso_data, cs_cycle_last));
+ DEFINE(VDSO_RAW_TIME_SEC, offsetof(struct vdso_data, raw_time_sec));
+ DEFINE(VDSO_RAW_TIME_NSEC, offsetof(struct vdso_data, raw_time_nsec));
DEFINE(VDSO_XTIME_CLK_SEC, offsetof(struct vdso_data, xtime_clock_sec));
DEFINE(VDSO_XTIME_CLK_NSEC, offsetof(struct vdso_data, xtime_clock_nsec));
DEFINE(VDSO_XTIME_CRS_SEC, offsetof(struct vdso_data, xtime_coarse_sec));
@@ -106,7 +109,8 @@ int main(void)
DEFINE(VDSO_WTM_CLK_SEC, offsetof(struct vdso_data, wtm_clock_sec));
DEFINE(VDSO_WTM_CLK_NSEC, offsetof(struct vdso_data, wtm_clock_nsec));
DEFINE(VDSO_TB_SEQ_COUNT, offsetof(struct vdso_data, tb_seq_count));
- DEFINE(VDSO_CS_MULT, offsetof(struct vdso_data, cs_mult));
+ DEFINE(VDSO_CS_MONO_MULT, offsetof(struct vdso_data, cs_mono_mult));
+ DEFINE(VDSO_CS_RAW_MULT, offsetof(struct vdso_data, cs_raw_mult));
DEFINE(VDSO_CS_SHIFT, offsetof(struct vdso_data, cs_shift));
DEFINE(VDSO_TZ_MINWEST, offsetof(struct vdso_data, tz_minuteswest));
DEFINE(VDSO_TZ_DSTTIME, offsetof(struct vdso_data, tz_dsttime));
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index cdf1dca64133..f75000996e4c 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -992,9 +992,9 @@ void __init setup_cpu_features(void)
if (!cwg)
pr_warn("No Cache Writeback Granule information, assuming cache line size %d\n",
cls);
- if (L1_CACHE_BYTES < cls)
- pr_warn("L1_CACHE_BYTES smaller than the Cache Writeback Granule (%d < %d)\n",
- L1_CACHE_BYTES, cls);
+ if (ARCH_DMA_MINALIGN < cls)
+ pr_warn("ARCH_DMA_MINALIGN smaller than the Cache Writeback Granule (%d < %d)\n",
+ ARCH_DMA_MINALIGN, cls);
}
static bool __maybe_unused
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 367a954f9937..f4dfd8c41e06 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -317,9 +317,21 @@ static int get_hbp_len(u8 hbp_len)
case ARM_BREAKPOINT_LEN_2:
len_in_bytes = 2;
break;
+ case ARM_BREAKPOINT_LEN_3:
+ len_in_bytes = 3;
+ break;
case ARM_BREAKPOINT_LEN_4:
len_in_bytes = 4;
break;
+ case ARM_BREAKPOINT_LEN_5:
+ len_in_bytes = 5;
+ break;
+ case ARM_BREAKPOINT_LEN_6:
+ len_in_bytes = 6;
+ break;
+ case ARM_BREAKPOINT_LEN_7:
+ len_in_bytes = 7;
+ break;
case ARM_BREAKPOINT_LEN_8:
len_in_bytes = 8;
break;
@@ -349,7 +361,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp)
* to generic breakpoint descriptions.
*/
int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
- int *gen_len, int *gen_type)
+ int *gen_len, int *gen_type, int *offset)
{
/* Type */
switch (ctrl.type) {
@@ -369,17 +381,33 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
return -EINVAL;
}
+ if (!ctrl.len)
+ return -EINVAL;
+ *offset = __ffs(ctrl.len);
+
/* Len */
- switch (ctrl.len) {
+ switch (ctrl.len >> *offset) {
case ARM_BREAKPOINT_LEN_1:
*gen_len = HW_BREAKPOINT_LEN_1;
break;
case ARM_BREAKPOINT_LEN_2:
*gen_len = HW_BREAKPOINT_LEN_2;
break;
+ case ARM_BREAKPOINT_LEN_3:
+ *gen_len = HW_BREAKPOINT_LEN_3;
+ break;
case ARM_BREAKPOINT_LEN_4:
*gen_len = HW_BREAKPOINT_LEN_4;
break;
+ case ARM_BREAKPOINT_LEN_5:
+ *gen_len = HW_BREAKPOINT_LEN_5;
+ break;
+ case ARM_BREAKPOINT_LEN_6:
+ *gen_len = HW_BREAKPOINT_LEN_6;
+ break;
+ case ARM_BREAKPOINT_LEN_7:
+ *gen_len = HW_BREAKPOINT_LEN_7;
+ break;
case ARM_BREAKPOINT_LEN_8:
*gen_len = HW_BREAKPOINT_LEN_8;
break;
@@ -423,9 +451,21 @@ static int arch_build_bp_info(struct perf_event *bp)
case HW_BREAKPOINT_LEN_2:
info->ctrl.len = ARM_BREAKPOINT_LEN_2;
break;
+ case HW_BREAKPOINT_LEN_3:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_3;
+ break;
case HW_BREAKPOINT_LEN_4:
info->ctrl.len = ARM_BREAKPOINT_LEN_4;
break;
+ case HW_BREAKPOINT_LEN_5:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_5;
+ break;
+ case HW_BREAKPOINT_LEN_6:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_6;
+ break;
+ case HW_BREAKPOINT_LEN_7:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_7;
+ break;
case HW_BREAKPOINT_LEN_8:
info->ctrl.len = ARM_BREAKPOINT_LEN_8;
break;
@@ -517,18 +557,17 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
default:
return -EINVAL;
}
-
- info->address &= ~alignment_mask;
- info->ctrl.len <<= offset;
} else {
if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE)
alignment_mask = 0x3;
else
alignment_mask = 0x7;
- if (info->address & alignment_mask)
- return -EINVAL;
+ offset = info->address & alignment_mask;
}
+ info->address &= ~alignment_mask;
+ info->ctrl.len <<= offset;
+
/*
* Disallow per-task kernel breakpoints since these would
* complicate the stepping code.
@@ -661,12 +700,47 @@ unlock:
}
NOKPROBE_SYMBOL(breakpoint_handler);
+/*
+ * Arm64 hardware does not always report a watchpoint hit address that matches
+ * one of the watchpoints set. It can also report an address "near" the
+ * watchpoint if a single instruction access both watched and unwatched
+ * addresses. There is no straight-forward way, short of disassembling the
+ * offending instruction, to map that address back to the watchpoint. This
+ * function computes the distance of the memory access from the watchpoint as a
+ * heuristic for the likelyhood that a given access triggered the watchpoint.
+ *
+ * See Section D2.10.5 "Determining the memory location that caused a Watchpoint
+ * exception" of ARMv8 Architecture Reference Manual for details.
+ *
+ * The function returns the distance of the address from the bytes watched by
+ * the watchpoint. In case of an exact match, it returns 0.
+ */
+static u64 get_distance_from_watchpoint(unsigned long addr, u64 val,
+ struct arch_hw_breakpoint_ctrl *ctrl)
+{
+ u64 wp_low, wp_high;
+ u32 lens, lene;
+
+ lens = __ffs(ctrl->len);
+ lene = __fls(ctrl->len);
+
+ wp_low = val + lens;
+ wp_high = val + lene;
+ if (addr < wp_low)
+ return wp_low - addr;
+ else if (addr > wp_high)
+ return addr - wp_high;
+ else
+ return 0;
+}
+
static int watchpoint_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
- int i, step = 0, *kernel_step, access;
+ int i, step = 0, *kernel_step, access, closest_match = 0;
+ u64 min_dist = -1, dist;
u32 ctrl_reg;
- u64 val, alignment_mask;
+ u64 val;
struct perf_event *wp, **slots;
struct debug_info *debug_info;
struct arch_hw_breakpoint *info;
@@ -675,35 +749,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
slots = this_cpu_ptr(wp_on_reg);
debug_info = &current->thread.debug;
+ /*
+ * Find all watchpoints that match the reported address. If no exact
+ * match is found. Attribute the hit to the closest watchpoint.
+ */
+ rcu_read_lock();
for (i = 0; i < core_num_wrps; ++i) {
- rcu_read_lock();
-
wp = slots[i];
-
if (wp == NULL)
- goto unlock;
-
- info = counter_arch_bp(wp);
- /* AArch32 watchpoints are either 4 or 8 bytes aligned. */
- if (is_compat_task()) {
- if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
- alignment_mask = 0x7;
- else
- alignment_mask = 0x3;
- } else {
- alignment_mask = 0x7;
- }
-
- /* Check if the watchpoint value matches. */
- val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
- if (val != (addr & ~alignment_mask))
- goto unlock;
-
- /* Possible match, check the byte address select to confirm. */
- ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
- decode_ctrl_reg(ctrl_reg, &ctrl);
- if (!((1 << (addr & alignment_mask)) & ctrl.len))
- goto unlock;
+ continue;
/*
* Check that the access type matches.
@@ -712,18 +766,41 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W :
HW_BREAKPOINT_R;
if (!(access & hw_breakpoint_type(wp)))
- goto unlock;
+ continue;
+ /* Check if the watchpoint value and byte select match. */
+ val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
+ ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
+ decode_ctrl_reg(ctrl_reg, &ctrl);
+ dist = get_distance_from_watchpoint(addr, val, &ctrl);
+ if (dist < min_dist) {
+ min_dist = dist;
+ closest_match = i;
+ }
+ /* Is this an exact match? */
+ if (dist != 0)
+ continue;
+
+ info = counter_arch_bp(wp);
info->trigger = addr;
perf_bp_event(wp, regs);
/* Do we need to handle the stepping? */
if (!wp->overflow_handler)
step = 1;
+ }
+ if (min_dist > 0 && min_dist != -1) {
+ /* No exact match found. */
+ wp = slots[closest_match];
+ info = counter_arch_bp(wp);
+ info->trigger = addr;
+ perf_bp_event(wp, regs);
-unlock:
- rcu_read_unlock();
+ /* Do we need to handle the stepping? */
+ if (!wp->overflow_handler)
+ step = 1;
}
+ rcu_read_unlock();
if (!step)
return 0;
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index c5ef05959813..6204b7600d1b 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -327,13 +327,13 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
struct arch_hw_breakpoint_ctrl ctrl,
struct perf_event_attr *attr)
{
- int err, len, type, disabled = !ctrl.enabled;
+ int err, len, type, offset, disabled = !ctrl.enabled;
attr->disabled = disabled;
if (disabled)
return 0;
- err = arch_bp_generic_fields(ctrl, &len, &type);
+ err = arch_bp_generic_fields(ctrl, &len, &type, &offset);
if (err)
return err;
@@ -352,6 +352,7 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
attr->bp_len = len;
attr->bp_type = type;
+ attr->bp_addr += offset;
return 0;
}
@@ -404,7 +405,7 @@ static int ptrace_hbp_get_addr(unsigned int note_type,
if (IS_ERR(bp))
return PTR_ERR(bp);
- *addr = bp ? bp->attr.bp_addr : 0;
+ *addr = bp ? counter_arch_bp(bp)->address : 0;
return 0;
}
diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
index 97bc68f4c689..3b8acfae7797 100644
--- a/arch/arm64/kernel/vdso.c
+++ b/arch/arm64/kernel/vdso.c
@@ -55,7 +55,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data;
*/
static struct page *vectors_page[1];
-static int alloc_vectors_page(void)
+static int __init alloc_vectors_page(void)
{
extern char __kuser_helper_start[], __kuser_helper_end[];
extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[];
@@ -88,7 +88,7 @@ int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp)
{
struct mm_struct *mm = current->mm;
unsigned long addr = AARCH32_VECTORS_BASE;
- static struct vm_special_mapping spec = {
+ static const struct vm_special_mapping spec = {
.name = "[vectors]",
.pages = vectors_page,
@@ -212,10 +212,16 @@ void update_vsyscall(struct timekeeper *tk)
vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec;
if (!use_syscall) {
+ /* tkr_mono.cycle_last == tkr_raw.cycle_last */
vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last;
+ vdso_data->raw_time_sec = tk->raw_time.tv_sec;
+ vdso_data->raw_time_nsec = tk->raw_time.tv_nsec;
vdso_data->xtime_clock_sec = tk->xtime_sec;
vdso_data->xtime_clock_nsec = tk->tkr_mono.xtime_nsec;
- vdso_data->cs_mult = tk->tkr_mono.mult;
+ /* tkr_raw.xtime_nsec == 0 */
+ vdso_data->cs_mono_mult = tk->tkr_mono.mult;
+ vdso_data->cs_raw_mult = tk->tkr_raw.mult;
+ /* tkr_mono.shift == tkr_raw.shift */
vdso_data->cs_shift = tk->tkr_mono.shift;
}
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
index b467fd0a384b..62c84f7cb01b 100644
--- a/arch/arm64/kernel/vdso/Makefile
+++ b/arch/arm64/kernel/vdso/Makefile
@@ -23,7 +23,7 @@ GCOV_PROFILE := n
ccflags-y += -Wl,-shared
obj-y += vdso.o
-extra-y += vdso.lds vdso-offsets.h
+extra-y += vdso.lds
CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
# Force dependency (incbin is bad)
@@ -42,11 +42,10 @@ $(obj)/%.so: $(obj)/%.so.dbg FORCE
gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh
quiet_cmd_vdsosym = VDSOSYM $@
define cmd_vdsosym
- $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ && \
- cp $@ include/generated/
+ $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
endef
-$(obj)/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE
+include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE
$(call if_changed,vdsosym)
# Assembly rules for the .S files
diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S
index 9f8eeccae67c..e00b4671bd7c 100644
--- a/arch/arm64/kernel/vdso/gettimeofday.S
+++ b/arch/arm64/kernel/vdso/gettimeofday.S
@@ -26,24 +26,109 @@
#define NSEC_PER_SEC_HI16 0x3b9a
vdso_data .req x6
-use_syscall .req w7
-seqcnt .req w8
+seqcnt .req w7
+w_tmp .req w8
+x_tmp .req x8
+
+/*
+ * Conventions for macro arguments:
+ * - An argument is write-only if its name starts with "res".
+ * - All other arguments are read-only, unless otherwise specified.
+ */
.macro seqcnt_acquire
9999: ldr seqcnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
tbnz seqcnt, #0, 9999b
dmb ishld
- ldr use_syscall, [vdso_data, #VDSO_USE_SYSCALL]
.endm
- .macro seqcnt_read, cnt
+ .macro seqcnt_check fail
dmb ishld
- ldr \cnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
+ ldr w_tmp, [vdso_data, #VDSO_TB_SEQ_COUNT]
+ cmp w_tmp, seqcnt
+ b.ne \fail
.endm
- .macro seqcnt_check, cnt, fail
- cmp \cnt, seqcnt
- b.ne \fail
+ .macro syscall_check fail
+ ldr w_tmp, [vdso_data, #VDSO_USE_SYSCALL]
+ cbnz w_tmp, \fail
+ .endm
+
+ .macro get_nsec_per_sec res
+ mov \res, #NSEC_PER_SEC_LO16
+ movk \res, #NSEC_PER_SEC_HI16, lsl #16
+ .endm
+
+ /*
+ * Returns the clock delta, in nanoseconds left-shifted by the clock
+ * shift.
+ */
+ .macro get_clock_shifted_nsec res, cycle_last, mult
+ /* Read the virtual counter. */
+ isb
+ mrs x_tmp, cntvct_el0
+ /* Calculate cycle delta and convert to ns. */
+ sub \res, x_tmp, \cycle_last
+ /* We can only guarantee 56 bits of precision. */
+ movn x_tmp, #0xff00, lsl #48
+ and \res, x_tmp, \res
+ mul \res, \res, \mult
+ .endm
+
+ /*
+ * Returns in res_{sec,nsec} the REALTIME timespec, based on the
+ * "wall time" (xtime) and the clock_mono delta.
+ */
+ .macro get_ts_realtime res_sec, res_nsec, \
+ clock_nsec, xtime_sec, xtime_nsec, nsec_to_sec
+ add \res_nsec, \clock_nsec, \xtime_nsec
+ udiv x_tmp, \res_nsec, \nsec_to_sec
+ add \res_sec, \xtime_sec, x_tmp
+ msub \res_nsec, x_tmp, \nsec_to_sec, \res_nsec
+ .endm
+
+ /*
+ * Returns in res_{sec,nsec} the timespec based on the clock_raw delta,
+ * used for CLOCK_MONOTONIC_RAW.
+ */
+ .macro get_ts_clock_raw res_sec, res_nsec, clock_nsec, nsec_to_sec
+ udiv \res_sec, \clock_nsec, \nsec_to_sec
+ msub \res_nsec, \res_sec, \nsec_to_sec, \clock_nsec
+ .endm
+
+ /* sec and nsec are modified in place. */
+ .macro add_ts sec, nsec, ts_sec, ts_nsec, nsec_to_sec
+ /* Add timespec. */
+ add \sec, \sec, \ts_sec
+ add \nsec, \nsec, \ts_nsec
+
+ /* Normalise the new timespec. */
+ cmp \nsec, \nsec_to_sec
+ b.lt 9999f
+ sub \nsec, \nsec, \nsec_to_sec
+ add \sec, \sec, #1
+9999:
+ cmp \nsec, #0
+ b.ge 9998f
+ add \nsec, \nsec, \nsec_to_sec
+ sub \sec, \sec, #1
+9998:
+ .endm
+
+ .macro clock_gettime_return, shift=0
+ .if \shift == 1
+ lsr x11, x11, x12
+ .endif
+ stp x10, x11, [x1, #TSPEC_TV_SEC]
+ mov x0, xzr
+ ret
+ .endm
+
+ .macro jump_slot jumptable, index, label
+ .if (. - \jumptable) != 4 * (\index)
+ .error "Jump slot index mismatch"
+ .endif
+ b \label
.endm
.text
@@ -51,18 +136,25 @@ seqcnt .req w8
/* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */
ENTRY(__kernel_gettimeofday)
.cfi_startproc
- mov x2, x30
- .cfi_register x30, x2
-
- /* Acquire the sequence counter and get the timespec. */
adr vdso_data, _vdso_data
-1: seqcnt_acquire
- cbnz use_syscall, 4f
-
/* If tv is NULL, skip to the timezone code. */
cbz x0, 2f
- bl __do_get_tspec
- seqcnt_check w9, 1b
+
+ /* Compute the time of day. */
+1: seqcnt_acquire
+ syscall_check fail=4f
+ ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
+ /* w11 = cs_mono_mult, w12 = cs_shift */
+ ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
+ ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
+ seqcnt_check fail=1b
+
+ get_nsec_per_sec res=x9
+ lsl x9, x9, x12
+
+ get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
+ get_ts_realtime res_sec=x10, res_nsec=x11, \
+ clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
/* Convert ns to us. */
mov x13, #1000
@@ -76,95 +168,126 @@ ENTRY(__kernel_gettimeofday)
stp w4, w5, [x1, #TZ_MINWEST]
3:
mov x0, xzr
- ret x2
+ ret
4:
/* Syscall fallback. */
mov x8, #__NR_gettimeofday
svc #0
- ret x2
+ ret
.cfi_endproc
ENDPROC(__kernel_gettimeofday)
+#define JUMPSLOT_MAX CLOCK_MONOTONIC_COARSE
+
/* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */
ENTRY(__kernel_clock_gettime)
.cfi_startproc
- cmp w0, #CLOCK_REALTIME
- ccmp w0, #CLOCK_MONOTONIC, #0x4, ne
- b.ne 2f
+ cmp w0, #JUMPSLOT_MAX
+ b.hi syscall
+ adr vdso_data, _vdso_data
+ adr x_tmp, jumptable
+ add x_tmp, x_tmp, w0, uxtw #2
+ br x_tmp
+
+ ALIGN
+jumptable:
+ jump_slot jumptable, CLOCK_REALTIME, realtime
+ jump_slot jumptable, CLOCK_MONOTONIC, monotonic
+ b syscall
+ b syscall
+ jump_slot jumptable, CLOCK_MONOTONIC_RAW, monotonic_raw
+ jump_slot jumptable, CLOCK_REALTIME_COARSE, realtime_coarse
+ jump_slot jumptable, CLOCK_MONOTONIC_COARSE, monotonic_coarse
+
+ .if (. - jumptable) != 4 * (JUMPSLOT_MAX + 1)
+ .error "Wrong jumptable size"
+ .endif
+
+ ALIGN
+realtime:
+ seqcnt_acquire
+ syscall_check fail=syscall
+ ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
+ /* w11 = cs_mono_mult, w12 = cs_shift */
+ ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
+ ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
+ seqcnt_check fail=realtime
- mov x2, x30
- .cfi_register x30, x2
+ /* All computations are done with left-shifted nsecs. */
+ get_nsec_per_sec res=x9
+ lsl x9, x9, x12
- /* Get kernel timespec. */
- adr vdso_data, _vdso_data
-1: seqcnt_acquire
- cbnz use_syscall, 7f
+ get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
+ get_ts_realtime res_sec=x10, res_nsec=x11, \
+ clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
+ clock_gettime_return, shift=1
- bl __do_get_tspec
- seqcnt_check w9, 1b
+ ALIGN
+monotonic:
+ seqcnt_acquire
+ syscall_check fail=syscall
+ ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
+ /* w11 = cs_mono_mult, w12 = cs_shift */
+ ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
+ ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
+ ldp x3, x4, [vdso_data, #VDSO_WTM_CLK_SEC]
+ seqcnt_check fail=monotonic
- mov x30, x2
+ /* All computations are done with left-shifted nsecs. */
+ lsl x4, x4, x12
+ get_nsec_per_sec res=x9
+ lsl x9, x9, x12
- cmp w0, #CLOCK_MONOTONIC
- b.ne 6f
+ get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
+ get_ts_realtime res_sec=x10, res_nsec=x11, \
+ clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
- /* Get wtm timespec. */
- ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
+ add_ts sec=x10, nsec=x11, ts_sec=x3, ts_nsec=x4, nsec_to_sec=x9
+ clock_gettime_return, shift=1
- /* Check the sequence counter. */
- seqcnt_read w9
- seqcnt_check w9, 1b
- b 4f
-2:
- cmp w0, #CLOCK_REALTIME_COARSE
- ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
- b.ne 8f
+ ALIGN
+monotonic_raw:
+ seqcnt_acquire
+ syscall_check fail=syscall
+ ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
+ /* w11 = cs_raw_mult, w12 = cs_shift */
+ ldp w12, w11, [vdso_data, #VDSO_CS_SHIFT]
+ ldp x13, x14, [vdso_data, #VDSO_RAW_TIME_SEC]
+ seqcnt_check fail=monotonic_raw
- /* xtime_coarse_nsec is already right-shifted */
- mov x12, #0
+ /* All computations are done with left-shifted nsecs. */
+ lsl x14, x14, x12
+ get_nsec_per_sec res=x9
+ lsl x9, x9, x12
- /* Get coarse timespec. */
- adr vdso_data, _vdso_data
-3: seqcnt_acquire
- ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC]
+ get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
+ get_ts_clock_raw res_sec=x10, res_nsec=x11, \
+ clock_nsec=x15, nsec_to_sec=x9
- /* Get wtm timespec. */
- ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
+ add_ts sec=x10, nsec=x11, ts_sec=x13, ts_nsec=x14, nsec_to_sec=x9
+ clock_gettime_return, shift=1
- /* Check the sequence counter. */
- seqcnt_read w9
- seqcnt_check w9, 3b
+ ALIGN
+realtime_coarse:
+ seqcnt_acquire
+ ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC]
+ seqcnt_check fail=realtime_coarse
+ clock_gettime_return
- cmp w0, #CLOCK_MONOTONIC_COARSE
- b.ne 6f
-4:
- /* Add on wtm timespec. */
- add x10, x10, x13
- lsl x14, x14, x12
- add x11, x11, x14
+ ALIGN
+monotonic_coarse:
+ seqcnt_acquire
+ ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC]
+ ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
+ seqcnt_check fail=monotonic_coarse
- /* Normalise the new timespec. */
- mov x15, #NSEC_PER_SEC_LO16
- movk x15, #NSEC_PER_SEC_HI16, lsl #16
- lsl x15, x15, x12
- cmp x11, x15
- b.lt 5f
- sub x11, x11, x15
- add x10, x10, #1
-5:
- cmp x11, #0
- b.ge 6f
- add x11, x11, x15
- sub x10, x10, #1
+ /* Computations are done in (non-shifted) nsecs. */
+ get_nsec_per_sec res=x9
+ add_ts sec=x10, nsec=x11, ts_sec=x13, ts_nsec=x14, nsec_to_sec=x9
+ clock_gettime_return
-6: /* Store to the user timespec. */
- lsr x11, x11, x12
- stp x10, x11, [x1, #TSPEC_TV_SEC]
- mov x0, xzr
- ret
-7:
- mov x30, x2
-8: /* Syscall fallback. */
+ ALIGN
+syscall: /* Syscall fallback. */
mov x8, #__NR_clock_gettime
svc #0
ret
@@ -176,6 +299,7 @@ ENTRY(__kernel_clock_getres)
.cfi_startproc
cmp w0, #CLOCK_REALTIME
ccmp w0, #CLOCK_MONOTONIC, #0x4, ne
+ ccmp w0, #CLOCK_MONOTONIC_RAW, #0x4, ne
b.ne 1f
ldr x2, 5f
@@ -203,55 +327,3 @@ ENTRY(__kernel_clock_getres)
.quad CLOCK_COARSE_RES
.cfi_endproc
ENDPROC(__kernel_clock_getres)
-
-/*
- * Read the current time from the architected counter.
- * Expects vdso_data to be initialised.
- * Clobbers the temporary registers (x9 - x15).
- * Returns:
- * - w9 = vDSO sequence counter
- * - (x10, x11) = (ts->tv_sec, shifted ts->tv_nsec)
- * - w12 = cs_shift
- */
-ENTRY(__do_get_tspec)
- .cfi_startproc
-
- /* Read the virtual counter. */
- isb
-#if IS_ENABLED(CONFIG_MSM_TIMER_LEAP)
-#define LEAST_32BITS 0x00000000FFFFFFFF
-reread:
- mrs x15, cntvct_el0
- and x13, x15, #LEAST_32BITS
- eor x13, x13, #LEAST_32BITS
- cbz x13, reread
-#else
- mrs x15, cntvct_el0
-#endif
-
- /* Read from the vDSO data page. */
- ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
- ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
- ldp w11, w12, [vdso_data, #VDSO_CS_MULT]
- seqcnt_read w9
-
- /* Calculate cycle delta and convert to ns. */
- sub x10, x15, x10
- /* We can only guarantee 56 bits of precision. */
- movn x15, #0xff00, lsl #48
- and x10, x15, x10
- mul x10, x10, x11
-
- /* Use the kernel time to calculate the new timespec. */
- mov x11, #NSEC_PER_SEC_LO16
- movk x11, #NSEC_PER_SEC_HI16, lsl #16
- lsl x11, x11, x12
- add x15, x10, x14
- udiv x14, x15, x11
- add x10, x13, x14
- mul x13, x14, x11
- sub x11, x15, x13
-
- ret
- .cfi_endproc
-ENDPROC(__do_get_tspec)
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index a13b9a65322f..1804aea44faa 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -708,5 +708,6 @@ int cpu_enable_pan(void *__unused)
int cpu_enable_uao(void *__unused)
{
asm(SET_PSTATE_UAO(1));
+ return 0;
}
#endif /* CONFIG_ARM64_UAO */
diff --git a/arch/c6x/kernel/ptrace.c b/arch/c6x/kernel/ptrace.c
index 3c494e84444d..a511ac16a8e3 100644
--- a/arch/c6x/kernel/ptrace.c
+++ b/arch/c6x/kernel/ptrace.c
@@ -69,46 +69,6 @@ static int gpr_get(struct task_struct *target,
0, sizeof(*regs));
}
-static int gpr_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
-{
- int ret;
- struct pt_regs *regs = task_pt_regs(target);
-
- /* Don't copyin TSR or CSR */
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &regs,
- 0, PT_TSR * sizeof(long));
- if (ret)
- return ret;
-
- ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
- PT_TSR * sizeof(long),
- (PT_TSR + 1) * sizeof(long));
- if (ret)
- return ret;
-
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &regs,
- (PT_TSR + 1) * sizeof(long),
- PT_CSR * sizeof(long));
- if (ret)
- return ret;
-
- ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
- PT_CSR * sizeof(long),
- (PT_CSR + 1) * sizeof(long));
- if (ret)
- return ret;
-
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &regs,
- (PT_CSR + 1) * sizeof(long), -1);
- return ret;
-}
-
enum c6x_regset {
REGSET_GPR,
};
@@ -120,7 +80,6 @@ static const struct user_regset c6x_regsets[] = {
.size = sizeof(u32),
.align = sizeof(u32),
.get = gpr_get,
- .set = gpr_set
},
};
diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c
index 92075544a19a..0dc1c8f622bc 100644
--- a/arch/h8300/kernel/ptrace.c
+++ b/arch/h8300/kernel/ptrace.c
@@ -95,7 +95,8 @@ static int regs_get(struct task_struct *target,
long *reg = (long *)&regs;
/* build user regs in buffer */
- for (r = 0; r < ARRAY_SIZE(register_offset); r++)
+ BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0);
+ for (r = 0; r < sizeof(regs) / sizeof(long); r++)
*reg++ = h8300_get_reg(target, r);
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
@@ -113,7 +114,8 @@ static int regs_set(struct task_struct *target,
long *reg;
/* build user regs in buffer */
- for (reg = (long *)&regs, r = 0; r < ARRAY_SIZE(register_offset); r++)
+ BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0);
+ for (reg = (long *)&regs, r = 0; r < sizeof(regs) / sizeof(long); r++)
*reg++ = h8300_get_reg(target, r);
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
@@ -122,7 +124,7 @@ static int regs_set(struct task_struct *target,
return ret;
/* write back to pt_regs */
- for (reg = (long *)&regs, r = 0; r < ARRAY_SIZE(register_offset); r++)
+ for (reg = (long *)&regs, r = 0; r < sizeof(regs) / sizeof(long); r++)
h8300_put_reg(target, r, *reg++);
return 0;
}
diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile
index 970d0bd99621..648f1cef33fa 100644
--- a/arch/ia64/Makefile
+++ b/arch/ia64/Makefile
@@ -95,8 +95,8 @@ define archhelp
echo '* unwcheck - Check vmlinux for invalid unwind info'
endef
-archprepare: make_nr_irqs_h FORCE
+archprepare: make_nr_irqs_h
PHONY += make_nr_irqs_h FORCE
-make_nr_irqs_h: FORCE
+make_nr_irqs_h:
$(Q)$(MAKE) $(build)=arch/ia64/kernel include/generated/nr-irqs.h
diff --git a/arch/metag/kernel/ptrace.c b/arch/metag/kernel/ptrace.c
index 7563628822bd..5e2dc7defd2c 100644
--- a/arch/metag/kernel/ptrace.c
+++ b/arch/metag/kernel/ptrace.c
@@ -24,6 +24,16 @@
* user_regset definitions.
*/
+static unsigned long user_txstatus(const struct pt_regs *regs)
+{
+ unsigned long data = (unsigned long)regs->ctx.Flags;
+
+ if (regs->ctx.SaveMask & TBICTX_CBUF_BIT)
+ data |= USER_GP_REGS_STATUS_CATCH_BIT;
+
+ return data;
+}
+
int metag_gp_regs_copyout(const struct pt_regs *regs,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
@@ -62,9 +72,7 @@ int metag_gp_regs_copyout(const struct pt_regs *regs,
if (ret)
goto out;
/* TXSTATUS */
- data = (unsigned long)regs->ctx.Flags;
- if (regs->ctx.SaveMask & TBICTX_CBUF_BIT)
- data |= USER_GP_REGS_STATUS_CATCH_BIT;
+ data = user_txstatus(regs);
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&data, 4*25, 4*26);
if (ret)
@@ -119,6 +127,7 @@ int metag_gp_regs_copyin(struct pt_regs *regs,
if (ret)
goto out;
/* TXSTATUS */
+ data = user_txstatus(regs);
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&data, 4*25, 4*26);
if (ret)
@@ -244,6 +253,8 @@ int metag_rp_state_copyin(struct pt_regs *regs,
unsigned long long *ptr;
int ret, i;
+ if (count < 4*13)
+ return -EINVAL;
/* Read the entire pipeline before making any changes */
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&rp, 0, 4*13);
@@ -303,7 +314,7 @@ static int metag_tls_set(struct task_struct *target,
const void *kbuf, const void __user *ubuf)
{
int ret;
- void __user *tls;
+ void __user *tls = target->thread.tls_ptr;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
if (ret)
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 74d581569778..c95bf18260f8 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -485,7 +485,8 @@ static int fpr_set(struct task_struct *target,
&target->thread.fpu,
0, sizeof(elf_fpregset_t));
- for (i = 0; i < NUM_FPU_REGS; i++) {
+ BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
+ for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) {
err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&fpr_val, i * sizeof(elf_fpreg_t),
(i + 1) * sizeof(elf_fpreg_t));
diff --git a/arch/powerpc/boot/zImage.lds.S b/arch/powerpc/boot/zImage.lds.S
index 861e72109df2..f080abfc2f83 100644
--- a/arch/powerpc/boot/zImage.lds.S
+++ b/arch/powerpc/boot/zImage.lds.S
@@ -68,6 +68,7 @@ SECTIONS
}
#ifdef CONFIG_PPC64_BOOT_WRAPPER
+ . = ALIGN(256);
.got :
{
__toc_start = .;
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
index 5cc2e7af3a7b..b379146de55b 100644
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -302,7 +302,6 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
advance = 0;
printk(KERN_ERR "Couldn't emulate instruction 0x%08x "
"(op %d xop %d)\n", inst, get_op(inst), get_xop(inst));
- kvmppc_core_queue_program(vcpu, 0);
}
}
diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c
index 3a40f718baef..4004e03267cd 100644
--- a/arch/s390/pci/pci_dma.c
+++ b/arch/s390/pci/pci_dma.c
@@ -455,7 +455,7 @@ int zpci_dma_init_device(struct zpci_dev *zdev)
zdev->dma_table = dma_alloc_cpu_table();
if (!zdev->dma_table) {
rc = -ENOMEM;
- goto out_clean;
+ goto out;
}
/*
@@ -475,18 +475,22 @@ int zpci_dma_init_device(struct zpci_dev *zdev)
zdev->iommu_bitmap = vzalloc(zdev->iommu_pages / 8);
if (!zdev->iommu_bitmap) {
rc = -ENOMEM;
- goto out_reg;
+ goto free_dma_table;
}
rc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
(u64) zdev->dma_table);
if (rc)
- goto out_reg;
- return 0;
+ goto free_bitmap;
-out_reg:
+ return 0;
+free_bitmap:
+ vfree(zdev->iommu_bitmap);
+ zdev->iommu_bitmap = NULL;
+free_dma_table:
dma_free_cpu_table(zdev->dma_table);
-out_clean:
+ zdev->dma_table = NULL;
+out:
return rc;
}
diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c
index 9ddc4928a089..c1566170964f 100644
--- a/arch/sparc/kernel/ptrace_64.c
+++ b/arch/sparc/kernel/ptrace_64.c
@@ -311,7 +311,7 @@ static int genregs64_set(struct task_struct *target,
}
if (!ret) {
- unsigned long y;
+ unsigned long y = regs->y;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&y,
diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c
index 440df0c7a2ee..a69321a77783 100644
--- a/arch/x86/crypto/ghash-clmulni-intel_glue.c
+++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c
@@ -219,6 +219,29 @@ static int ghash_async_final(struct ahash_request *req)
}
}
+static int ghash_async_import(struct ahash_request *req, const void *in)
+{
+ struct ahash_request *cryptd_req = ahash_request_ctx(req);
+ struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
+ struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ ghash_async_init(req);
+ memcpy(dctx, in, sizeof(*dctx));
+ return 0;
+
+}
+
+static int ghash_async_export(struct ahash_request *req, void *out)
+{
+ struct ahash_request *cryptd_req = ahash_request_ctx(req);
+ struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
+ struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ memcpy(out, dctx, sizeof(*dctx));
+ return 0;
+
+}
+
static int ghash_async_digest(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
@@ -288,8 +311,11 @@ static struct ahash_alg ghash_async_alg = {
.final = ghash_async_final,
.setkey = ghash_async_setkey,
.digest = ghash_async_digest,
+ .export = ghash_async_export,
+ .import = ghash_async_import,
.halg = {
.digestsize = GHASH_DIGEST_SIZE,
+ .statesize = sizeof(struct ghash_desc_ctx),
.base = {
.cra_name = "ghash",
.cra_driver_name = "ghash-clmulni",
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 265c0ed68118..7af017a8958f 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -187,10 +187,10 @@ vdso_img_insttargets := $(vdso_img_sodbg:%.dbg=install_%)
$(MODLIB)/vdso: FORCE
@mkdir -p $(MODLIB)/vdso
-$(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso FORCE
+$(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso
$(call cmd,vdso_install)
PHONY += vdso_install $(vdso_img_insttargets)
-vdso_install: $(vdso_img_insttargets) FORCE
+vdso_install: $(vdso_img_insttargets)
clean-files := vdso32.so vdso32.so.dbg vdso64* vdso-image-*.c vdsox32.so*
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index cfc4a966e2b9..83b5f7a323a9 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -30,6 +30,7 @@
#include <asm/apic.h>
#include <asm/timer.h>
#include <asm/reboot.h>
+#include <asm/nmi.h>
struct ms_hyperv_info ms_hyperv;
EXPORT_SYMBOL_GPL(ms_hyperv);
@@ -157,6 +158,26 @@ static unsigned char hv_get_nmi_reason(void)
return 0;
}
+#ifdef CONFIG_X86_LOCAL_APIC
+/*
+ * Prior to WS2016 Debug-VM sends NMIs to all CPUs which makes
+ * it dificult to process CHANNELMSG_UNLOAD in case of crash. Handle
+ * unknown NMI on the first CPU which gets it.
+ */
+static int hv_nmi_unknown(unsigned int val, struct pt_regs *regs)
+{
+ static atomic_t nmi_cpu = ATOMIC_INIT(-1);
+
+ if (!unknown_nmi_panic)
+ return NMI_DONE;
+
+ if (atomic_cmpxchg(&nmi_cpu, -1, raw_smp_processor_id()) != -1)
+ return NMI_HANDLED;
+
+ return NMI_DONE;
+}
+#endif
+
static void __init ms_hyperv_init_platform(void)
{
/*
@@ -182,6 +203,9 @@ static void __init ms_hyperv_init_platform(void)
printk(KERN_INFO "HyperV: LAPIC Timer Frequency: %#x\n",
lapic_timer_frequency);
}
+
+ register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST,
+ "hv_nmi_unknown");
#endif
if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 1a8256dd6729..5b2f2306fbcc 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -1996,8 +1996,8 @@ static int x86_pmu_event_init(struct perf_event *event)
static void refresh_pce(void *ignored)
{
- if (current->mm)
- load_mm_cr4(current->mm);
+ if (current->active_mm)
+ load_mm_cr4(current->active_mm);
}
static void x86_pmu_event_mapped(struct perf_event *event)
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index f129a9af6357..b6b0077da1af 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -4,6 +4,7 @@
* Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
*/
+#define DISABLE_BRANCH_PROFILING
#include <linux/init.h>
#include <linux/linkage.h>
#include <linux/types.h>
diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c
index d470cf219a2d..4e5ac46adc9d 100644
--- a/arch/x86/mm/kasan_init_64.c
+++ b/arch/x86/mm/kasan_init_64.c
@@ -1,3 +1,4 @@
+#define DISABLE_BRANCH_PROFILING
#define pr_fmt(fmt) "kasan: " fmt
#include <linux/bootmem.h>
#include <linux/kasan.h>
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c
index c6d6efed392a..7575f0798194 100644
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -231,23 +231,14 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
return 1;
for_each_pci_msi_entry(msidesc, dev) {
- __pci_read_msi_msg(msidesc, &msg);
- pirq = MSI_ADDR_EXT_DEST_ID(msg.address_hi) |
- ((msg.address_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff);
- if (msg.data != XEN_PIRQ_MSI_DATA ||
- xen_irq_from_pirq(pirq) < 0) {
- pirq = xen_allocate_pirq_msi(dev, msidesc);
- if (pirq < 0) {
- irq = -ENODEV;
- goto error;
- }
- xen_msi_compose_msg(dev, pirq, &msg);
- __pci_write_msi_msg(msidesc, &msg);
- dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq);
- } else {
- dev_dbg(&dev->dev,
- "xen: msi already bound to pirq=%d\n", pirq);
+ pirq = xen_allocate_pirq_msi(dev, msidesc);
+ if (pirq < 0) {
+ irq = -ENODEV;
+ goto error;
}
+ xen_msi_compose_msg(dev, pirq, &msg);
+ __pci_write_msi_msg(msidesc, &msg);
+ dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq);
irq = xen_bind_pirq_msi_to_irq(dev, msidesc, pirq,
(type == PCI_CAP_ID_MSI) ? nvec : 1,
(type == PCI_CAP_ID_MSIX) ?
diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c
index 9e2ba5c6e1dd..f42e78de1e10 100644
--- a/arch/x86/xen/spinlock.c
+++ b/arch/x86/xen/spinlock.c
@@ -27,6 +27,12 @@ static bool xen_pvspin = true;
static void xen_qlock_kick(int cpu)
{
+ int irq = per_cpu(lock_kicker_irq, cpu);
+
+ /* Don't kick if the target's kicker interrupt is not initialized. */
+ if (irq == -1)
+ return;
+
xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR);
}
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index 0774799942e0..c6fee7437be4 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -182,6 +182,9 @@ static void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter)
__set_bit(WRITE_16, filter->write_ok);
__set_bit(WRITE_LONG, filter->write_ok);
__set_bit(WRITE_LONG_2, filter->write_ok);
+ __set_bit(WRITE_SAME, filter->write_ok);
+ __set_bit(WRITE_SAME_16, filter->write_ok);
+ __set_bit(WRITE_SAME_32, filter->write_ok);
__set_bit(ERASE, filter->write_ok);
__set_bit(GPCMD_MODE_SELECT_10, filter->write_ok);
__set_bit(MODE_SELECT, filter->write_ok);
diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm
new file mode 100644
index 000000000000..866da9361b71
--- /dev/null
+++ b/build.config.goldfish.arm
@@ -0,0 +1,12 @@
+ARCH=arm
+BRANCH=android-4.4
+CROSS_COMPILE=arm-linux-androidkernel-
+DEFCONFIG=ranchu_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin
+FILES="
+arch/arm/boot/zImage
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64
new file mode 100644
index 000000000000..9c963cf4a3d8
--- /dev/null
+++ b/build.config.goldfish.arm64
@@ -0,0 +1,12 @@
+ARCH=arm64
+BRANCH=android-4.4
+CROSS_COMPILE=aarch64-linux-android-
+DEFCONFIG=ranchu64_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin
+FILES="
+arch/arm64/boot/Image
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips
new file mode 100644
index 000000000000..8af53d2c2940
--- /dev/null
+++ b/build.config.goldfish.mips
@@ -0,0 +1,11 @@
+ARCH=mips
+BRANCH=android-4.4
+CROSS_COMPILE=mips64el-linux-android-
+DEFCONFIG=ranchu_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin
+FILES="
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64
new file mode 100644
index 000000000000..2a33d36dc4c8
--- /dev/null
+++ b/build.config.goldfish.mips64
@@ -0,0 +1,11 @@
+ARCH=mips
+BRANCH=android-4.4
+CROSS_COMPILE=mips64el-linux-android-
+DEFCONFIG=ranchu64_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin
+FILES="
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86
new file mode 100644
index 000000000000..f86253f58d4d
--- /dev/null
+++ b/build.config.goldfish.x86
@@ -0,0 +1,12 @@
+ARCH=x86
+BRANCH=android-4.4
+CROSS_COMPILE=x86_64-linux-android-
+DEFCONFIG=i386_ranchu_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin
+FILES="
+arch/x86/boot/bzImage
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64
new file mode 100644
index 000000000000..e1738861ec5c
--- /dev/null
+++ b/build.config.goldfish.x86_64
@@ -0,0 +1,12 @@
+ARCH=x86_64
+BRANCH=android-4.4
+CROSS_COMPILE=x86_64-linux-android-
+DEFCONFIG=x86_64_ranchu_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin
+FILES="
+arch/x86/boot/bzImage
+vmlinux
+System.map
+"
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 7240821137fd..3240d394426c 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -289,6 +289,24 @@ config CRYPTO_CBC
CBC: Cipher Block Chaining mode
This block cipher algorithm is required for IPSec.
+config CRYPTO_HEH
+ tristate "HEH support"
+ select CRYPTO_CMAC
+ select CRYPTO_ECB
+ select CRYPTO_GF128MUL
+ select CRYPTO_MANAGER
+ select CRYPTO_POLY_HASH_ARM64_CE if ARM64 && KERNEL_MODE_NEON
+ help
+ HEH: Hash-Encrypt-Hash mode
+ HEH is a proposed block cipher mode of operation which extends the
+ strong pseudo-random permutation (SPRP) property of block ciphers to
+ arbitrary-length input strings. This provides a stronger notion of
+ security than existing block cipher modes of operation (e.g. CBC, CTR,
+ XTS), though it is usually less performant. Applications include disk
+ encryption and encryption of file names and contents. Currently, this
+ implementation only provides a symmetric cipher interface, so it can't
+ yet be used as an AEAD.
+
config CRYPTO_CTR
tristate "CTR support"
select CRYPTO_BLKCIPHER
diff --git a/crypto/Makefile b/crypto/Makefile
index 03e66097eb0c..8507d1fab3ac 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_CRYPTO_TGR192) += tgr192.o
obj-$(CONFIG_CRYPTO_GF128MUL) += gf128mul.o
obj-$(CONFIG_CRYPTO_ECB) += ecb.o
obj-$(CONFIG_CRYPTO_CBC) += cbc.o
+obj-$(CONFIG_CRYPTO_HEH) += heh.o
obj-$(CONFIG_CRYPTO_PCBC) += pcbc.o
obj-$(CONFIG_CRYPTO_CTS) += cts.o
obj-$(CONFIG_CRYPTO_LRW) += lrw.o
diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
index 68a5ceaa04c8..8d8b3eeba725 100644
--- a/crypto/algif_hash.c
+++ b/crypto/algif_hash.c
@@ -184,7 +184,7 @@ static int hash_accept(struct socket *sock, struct socket *newsock, int flags)
struct alg_sock *ask = alg_sk(sk);
struct hash_ctx *ctx = ask->private;
struct ahash_request *req = &ctx->req;
- char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))];
+ char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req)) ? : 1];
struct sock *sk2;
struct alg_sock *ask2;
struct hash_ctx *ctx2;
diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c
index dca7bc87dad9..7bbfadc195a6 100644
--- a/crypto/blkcipher.c
+++ b/crypto/blkcipher.c
@@ -373,6 +373,27 @@ int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc,
}
EXPORT_SYMBOL_GPL(blkcipher_aead_walk_virt_block);
+/*
+ * This function allows ablkcipher algorithms to use the blkcipher_walk API to
+ * walk over their data. The specified crypto_ablkcipher tfm is used to
+ * initialize the struct blkcipher_walk, and the crypto_blkcipher specified in
+ * desc->tfm is never used so it can be left NULL. (Yes, this design is ugly,
+ * but it parallels blkcipher_aead_walk_virt_block() above. In the 4.10 kernel
+ * this is starting to be cleaned up...)
+ */
+int blkcipher_ablkcipher_walk_virt(struct blkcipher_desc *desc,
+ struct blkcipher_walk *walk,
+ struct crypto_ablkcipher *tfm)
+{
+ walk->flags &= ~BLKCIPHER_WALK_PHYS;
+ walk->walk_blocksize = crypto_ablkcipher_blocksize(tfm);
+ walk->cipher_blocksize = walk->walk_blocksize;
+ walk->ivsize = crypto_ablkcipher_ivsize(tfm);
+ walk->alignmask = crypto_ablkcipher_alignmask(tfm);
+ return blkcipher_walk_first(desc, walk);
+}
+EXPORT_SYMBOL_GPL(blkcipher_ablkcipher_walk_virt);
+
static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key,
unsigned int keylen)
{
diff --git a/crypto/cryptd.c b/crypto/cryptd.c
index e7aa904cb20b..26a504db3f53 100644
--- a/crypto/cryptd.c
+++ b/crypto/cryptd.c
@@ -642,6 +642,7 @@ static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb,
inst->alg.halg.base.cra_flags = type;
inst->alg.halg.digestsize = salg->digestsize;
+ inst->alg.halg.statesize = salg->statesize;
inst->alg.halg.base.cra_ctxsize = sizeof(struct cryptd_hash_ctx);
inst->alg.halg.base.cra_init = cryptd_hash_init_tfm;
diff --git a/crypto/gf128mul.c b/crypto/gf128mul.c
index 5276607c72d0..f3d9f6da0767 100644
--- a/crypto/gf128mul.c
+++ b/crypto/gf128mul.c
@@ -44,7 +44,7 @@
---------------------------------------------------------------------------
Issue 31/01/2006
- This file provides fast multiplication in GF(128) as required by several
+ This file provides fast multiplication in GF(2^128) as required by several
cryptographic authentication modes
*/
@@ -88,37 +88,52 @@
q(0xf8), q(0xf9), q(0xfa), q(0xfb), q(0xfc), q(0xfd), q(0xfe), q(0xff) \
}
-/* Given the value i in 0..255 as the byte overflow when a field element
- in GHASH is multiplied by x^8, this function will return the values that
- are generated in the lo 16-bit word of the field value by applying the
- modular polynomial. The values lo_byte and hi_byte are returned via the
- macro xp_fun(lo_byte, hi_byte) so that the values can be assembled into
- memory as required by a suitable definition of this macro operating on
- the table above
-*/
-
-#define xx(p, q) 0x##p##q
+/*
+ * Given a value i in 0..255 as the byte overflow when a field element
+ * in GF(2^128) is multiplied by x^8, the following macro returns the
+ * 16-bit value that must be XOR-ed into the low-degree end of the
+ * product to reduce it modulo the irreducible polynomial x^128 + x^7 +
+ * x^2 + x + 1.
+ *
+ * There are two versions of the macro, and hence two tables: one for
+ * the "be" convention where the highest-order bit is the coefficient of
+ * the highest-degree polynomial term, and one for the "le" convention
+ * where the highest-order bit is the coefficient of the lowest-degree
+ * polynomial term. In both cases the values are stored in CPU byte
+ * endianness such that the coefficients are ordered consistently across
+ * bytes, i.e. in the "be" table bits 15..0 of the stored value
+ * correspond to the coefficients of x^15..x^0, and in the "le" table
+ * bits 15..0 correspond to the coefficients of x^0..x^15.
+ *
+ * Therefore, provided that the appropriate byte endianness conversions
+ * are done by the multiplication functions (and these must be in place
+ * anyway to support both little endian and big endian CPUs), the "be"
+ * table can be used for multiplications of both "bbe" and "ble"
+ * elements, and the "le" table can be used for multiplications of both
+ * "lle" and "lbe" elements.
+ */
-#define xda_bbe(i) ( \
- (i & 0x80 ? xx(43, 80) : 0) ^ (i & 0x40 ? xx(21, c0) : 0) ^ \
- (i & 0x20 ? xx(10, e0) : 0) ^ (i & 0x10 ? xx(08, 70) : 0) ^ \
- (i & 0x08 ? xx(04, 38) : 0) ^ (i & 0x04 ? xx(02, 1c) : 0) ^ \
- (i & 0x02 ? xx(01, 0e) : 0) ^ (i & 0x01 ? xx(00, 87) : 0) \
+#define xda_be(i) ( \
+ (i & 0x80 ? 0x4380 : 0) ^ (i & 0x40 ? 0x21c0 : 0) ^ \
+ (i & 0x20 ? 0x10e0 : 0) ^ (i & 0x10 ? 0x0870 : 0) ^ \
+ (i & 0x08 ? 0x0438 : 0) ^ (i & 0x04 ? 0x021c : 0) ^ \
+ (i & 0x02 ? 0x010e : 0) ^ (i & 0x01 ? 0x0087 : 0) \
)
-#define xda_lle(i) ( \
- (i & 0x80 ? xx(e1, 00) : 0) ^ (i & 0x40 ? xx(70, 80) : 0) ^ \
- (i & 0x20 ? xx(38, 40) : 0) ^ (i & 0x10 ? xx(1c, 20) : 0) ^ \
- (i & 0x08 ? xx(0e, 10) : 0) ^ (i & 0x04 ? xx(07, 08) : 0) ^ \
- (i & 0x02 ? xx(03, 84) : 0) ^ (i & 0x01 ? xx(01, c2) : 0) \
+#define xda_le(i) ( \
+ (i & 0x80 ? 0xe100 : 0) ^ (i & 0x40 ? 0x7080 : 0) ^ \
+ (i & 0x20 ? 0x3840 : 0) ^ (i & 0x10 ? 0x1c20 : 0) ^ \
+ (i & 0x08 ? 0x0e10 : 0) ^ (i & 0x04 ? 0x0708 : 0) ^ \
+ (i & 0x02 ? 0x0384 : 0) ^ (i & 0x01 ? 0x01c2 : 0) \
)
-static const u16 gf128mul_table_lle[256] = gf128mul_dat(xda_lle);
-static const u16 gf128mul_table_bbe[256] = gf128mul_dat(xda_bbe);
+static const u16 gf128mul_table_le[256] = gf128mul_dat(xda_le);
+static const u16 gf128mul_table_be[256] = gf128mul_dat(xda_be);
-/* These functions multiply a field element by x, by x^4 and by x^8
- * in the polynomial field representation. It uses 32-bit word operations
- * to gain speed but compensates for machine endianess and hence works
+/*
+ * The following functions multiply a field element by x or by x^8 in
+ * the polynomial field representation. They use 64-bit word operations
+ * to gain speed but compensate for machine endianness and hence work
* correctly on both styles of machine.
*/
@@ -126,7 +141,7 @@ static void gf128mul_x_lle(be128 *r, const be128 *x)
{
u64 a = be64_to_cpu(x->a);
u64 b = be64_to_cpu(x->b);
- u64 _tt = gf128mul_table_lle[(b << 7) & 0xff];
+ u64 _tt = gf128mul_table_le[(b << 7) & 0xff];
r->b = cpu_to_be64((b >> 1) | (a << 63));
r->a = cpu_to_be64((a >> 1) ^ (_tt << 48));
@@ -136,7 +151,7 @@ static void gf128mul_x_bbe(be128 *r, const be128 *x)
{
u64 a = be64_to_cpu(x->a);
u64 b = be64_to_cpu(x->b);
- u64 _tt = gf128mul_table_bbe[a >> 63];
+ u64 _tt = gf128mul_table_be[a >> 63];
r->a = cpu_to_be64((a << 1) | (b >> 63));
r->b = cpu_to_be64((b << 1) ^ _tt);
@@ -146,7 +161,7 @@ void gf128mul_x_ble(be128 *r, const be128 *x)
{
u64 a = le64_to_cpu(x->a);
u64 b = le64_to_cpu(x->b);
- u64 _tt = gf128mul_table_bbe[b >> 63];
+ u64 _tt = gf128mul_table_be[b >> 63];
r->a = cpu_to_le64((a << 1) ^ _tt);
r->b = cpu_to_le64((b << 1) | (a >> 63));
@@ -157,7 +172,7 @@ static void gf128mul_x8_lle(be128 *x)
{
u64 a = be64_to_cpu(x->a);
u64 b = be64_to_cpu(x->b);
- u64 _tt = gf128mul_table_lle[b & 0xff];
+ u64 _tt = gf128mul_table_le[b & 0xff];
x->b = cpu_to_be64((b >> 8) | (a << 56));
x->a = cpu_to_be64((a >> 8) ^ (_tt << 48));
@@ -167,12 +182,22 @@ static void gf128mul_x8_bbe(be128 *x)
{
u64 a = be64_to_cpu(x->a);
u64 b = be64_to_cpu(x->b);
- u64 _tt = gf128mul_table_bbe[a >> 56];
+ u64 _tt = gf128mul_table_be[a >> 56];
x->a = cpu_to_be64((a << 8) | (b >> 56));
x->b = cpu_to_be64((b << 8) ^ _tt);
}
+static void gf128mul_x8_ble(be128 *x)
+{
+ u64 a = le64_to_cpu(x->b);
+ u64 b = le64_to_cpu(x->a);
+ u64 _tt = gf128mul_table_be[a >> 56];
+
+ x->b = cpu_to_le64((a << 8) | (b >> 56));
+ x->a = cpu_to_le64((b << 8) ^ _tt);
+}
+
void gf128mul_lle(be128 *r, const be128 *b)
{
be128 p[8];
@@ -249,9 +274,48 @@ void gf128mul_bbe(be128 *r, const be128 *b)
}
EXPORT_SYMBOL(gf128mul_bbe);
+void gf128mul_ble(be128 *r, const be128 *b)
+{
+ be128 p[8];
+ int i;
+
+ p[0] = *r;
+ for (i = 0; i < 7; ++i)
+ gf128mul_x_ble((be128 *)&p[i + 1], (be128 *)&p[i]);
+
+ memset(r, 0, sizeof(*r));
+ for (i = 0;;) {
+ u8 ch = ((u8 *)b)[15 - i];
+
+ if (ch & 0x80)
+ be128_xor(r, r, &p[7]);
+ if (ch & 0x40)
+ be128_xor(r, r, &p[6]);
+ if (ch & 0x20)
+ be128_xor(r, r, &p[5]);
+ if (ch & 0x10)
+ be128_xor(r, r, &p[4]);
+ if (ch & 0x08)
+ be128_xor(r, r, &p[3]);
+ if (ch & 0x04)
+ be128_xor(r, r, &p[2]);
+ if (ch & 0x02)
+ be128_xor(r, r, &p[1]);
+ if (ch & 0x01)
+ be128_xor(r, r, &p[0]);
+
+ if (++i >= 16)
+ break;
+
+ gf128mul_x8_ble(r);
+ }
+}
+EXPORT_SYMBOL(gf128mul_ble);
+
+
/* This version uses 64k bytes of table space.
A 16 byte buffer has to be multiplied by a 16 byte key
- value in GF(128). If we consider a GF(128) value in
+ value in GF(2^128). If we consider a GF(2^128) value in
the buffer's lowest byte, we can construct a table of
the 256 16 byte values that result from the 256 values
of this byte. This requires 4096 bytes. But we also
@@ -352,8 +416,8 @@ void gf128mul_free_64k(struct gf128mul_64k *t)
int i;
for (i = 0; i < 16; i++)
- kfree(t->t[i]);
- kfree(t);
+ kzfree(t->t[i]);
+ kzfree(t);
}
EXPORT_SYMBOL(gf128mul_free_64k);
@@ -385,7 +449,7 @@ EXPORT_SYMBOL(gf128mul_64k_bbe);
/* This version uses 4k bytes of table space.
A 16 byte buffer has to be multiplied by a 16 byte key
- value in GF(128). If we consider a GF(128) value in a
+ value in GF(2^128). If we consider a GF(2^128) value in a
single byte, we can construct a table of the 256 16 byte
values that result from the 256 values of this byte.
This requires 4096 bytes. If we take the highest byte in
@@ -443,6 +507,28 @@ out:
}
EXPORT_SYMBOL(gf128mul_init_4k_bbe);
+struct gf128mul_4k *gf128mul_init_4k_ble(const be128 *g)
+{
+ struct gf128mul_4k *t;
+ int j, k;
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ goto out;
+
+ t->t[1] = *g;
+ for (j = 1; j <= 64; j <<= 1)
+ gf128mul_x_ble(&t->t[j + j], &t->t[j]);
+
+ for (j = 2; j < 256; j += j)
+ for (k = 1; k < j; ++k)
+ be128_xor(&t->t[j + k], &t->t[j], &t->t[k]);
+
+out:
+ return t;
+}
+EXPORT_SYMBOL(gf128mul_init_4k_ble);
+
void gf128mul_4k_lle(be128 *a, struct gf128mul_4k *t)
{
u8 *ap = (u8 *)a;
@@ -473,5 +559,20 @@ void gf128mul_4k_bbe(be128 *a, struct gf128mul_4k *t)
}
EXPORT_SYMBOL(gf128mul_4k_bbe);
+void gf128mul_4k_ble(be128 *a, struct gf128mul_4k *t)
+{
+ u8 *ap = (u8 *)a;
+ be128 r[1];
+ int i = 15;
+
+ *r = t->t[ap[15]];
+ while (i--) {
+ gf128mul_x8_ble(r);
+ be128_xor(r, r, &t->t[ap[i]]);
+ }
+ *a = *r;
+}
+EXPORT_SYMBOL(gf128mul_4k_ble);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Functions for multiplying elements of GF(2^128)");
diff --git a/crypto/heh.c b/crypto/heh.c
new file mode 100644
index 000000000000..10c00aaf797e
--- /dev/null
+++ b/crypto/heh.c
@@ -0,0 +1,1033 @@
+/*
+ * HEH: Hash-Encrypt-Hash mode
+ *
+ * Copyright (c) 2016 Google Inc.
+ *
+ * Authors:
+ * Alex Cope <alexcope@google.com>
+ * Eric Biggers <ebiggers@google.com>
+ */
+
+/*
+ * Hash-Encrypt-Hash (HEH) is a proposed block cipher mode of operation which
+ * extends the strong pseudo-random permutation (SPRP) property of block ciphers
+ * (e.g. AES) to arbitrary length input strings. It uses two keyed invertible
+ * hash functions with a layer of ECB encryption applied in-between. The
+ * algorithm is specified by the following Internet Draft:
+ *
+ * https://tools.ietf.org/html/draft-cope-heh-01
+ *
+ * Although HEH can be used as either a regular symmetric cipher or as an AEAD,
+ * currently this module only provides it as a symmetric cipher. Additionally,
+ * only 16-byte nonces are supported.
+ */
+
+#include <crypto/gf128mul.h>
+#include <crypto/internal/hash.h>
+#include <crypto/internal/skcipher.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/skcipher.h>
+#include "internal.h"
+
+/*
+ * The block size is the size of GF(2^128) elements and also the required block
+ * size of the underlying block cipher.
+ */
+#define HEH_BLOCK_SIZE 16
+
+struct heh_instance_ctx {
+ struct crypto_shash_spawn cmac;
+ struct crypto_shash_spawn poly_hash;
+ struct crypto_skcipher_spawn ecb;
+};
+
+struct heh_tfm_ctx {
+ struct crypto_shash *cmac;
+ struct crypto_shash *poly_hash; /* keyed with tau_key */
+ struct crypto_ablkcipher *ecb;
+};
+
+struct heh_cmac_data {
+ u8 nonce[HEH_BLOCK_SIZE];
+ __le32 nonce_length;
+ __le32 aad_length;
+ __le32 message_length;
+ __le32 padding;
+};
+
+struct heh_req_ctx { /* aligned to alignmask */
+ be128 beta1_key;
+ be128 beta2_key;
+ union {
+ struct {
+ struct heh_cmac_data data;
+ struct shash_desc desc;
+ /* + crypto_shash_descsize(cmac) */
+ } cmac;
+ struct {
+ struct shash_desc desc;
+ /* + crypto_shash_descsize(poly_hash) */
+ } poly_hash;
+ struct {
+ u8 keystream[HEH_BLOCK_SIZE];
+ u8 tmp[HEH_BLOCK_SIZE];
+ struct scatterlist tmp_sgl[2];
+ struct ablkcipher_request req;
+ /* + crypto_ablkcipher_reqsize(ecb) */
+ } ecb;
+ } u;
+};
+
+/*
+ * Get the offset in bytes to the last full block, or equivalently the length of
+ * all full blocks excluding the last
+ */
+static inline unsigned int get_tail_offset(unsigned int len)
+{
+ len -= len % HEH_BLOCK_SIZE;
+ return len - HEH_BLOCK_SIZE;
+}
+
+static inline struct heh_req_ctx *heh_req_ctx(struct ablkcipher_request *req)
+{
+ unsigned int alignmask = crypto_ablkcipher_alignmask(
+ crypto_ablkcipher_reqtfm(req));
+
+ return (void *)PTR_ALIGN((u8 *)ablkcipher_request_ctx(req),
+ alignmask + 1);
+}
+
+static inline void async_done(struct crypto_async_request *areq, int err,
+ int (*next_step)(struct ablkcipher_request *,
+ u32))
+{
+ struct ablkcipher_request *req = areq->data;
+
+ if (err)
+ goto out;
+
+ err = next_step(req, req->base.flags & ~CRYPTO_TFM_REQ_MAY_SLEEP);
+ if (err == -EINPROGRESS ||
+ (err == -EBUSY && (req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return;
+out:
+ ablkcipher_request_complete(req, err);
+}
+
+/*
+ * Generate the per-message "beta" keys used by the hashing layers of HEH. The
+ * first beta key is the CMAC of the nonce, the additional authenticated data
+ * (AAD), and the lengths in bytes of the nonce, AAD, and message. The nonce
+ * and AAD are each zero-padded to the next 16-byte block boundary, and the
+ * lengths are serialized as 4-byte little endian integers and zero-padded to
+ * the next 16-byte block boundary.
+ * The second beta key is the first one interpreted as an element in GF(2^128)
+ * and multiplied by x.
+ *
+ * Note that because the nonce and AAD may, in general, be variable-length, the
+ * key generation must be done by a pseudo-random function (PRF) on
+ * variable-length inputs. CBC-MAC does not satisfy this, as it is only a PRF
+ * on fixed-length inputs. CMAC remedies this flaw. Including the lengths of
+ * the nonce, AAD, and message is also critical to avoid collisions.
+ *
+ * That being said, this implementation does not yet operate as an AEAD and
+ * therefore there is never any AAD, nor are variable-length nonces supported.
+ */
+static int generate_betas(struct ablkcipher_request *req,
+ be128 *beta1_key, be128 *beta2_key)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct heh_req_ctx *rctx = heh_req_ctx(req);
+ struct heh_cmac_data *data = &rctx->u.cmac.data;
+ struct shash_desc *desc = &rctx->u.cmac.desc;
+ int err;
+
+ BUILD_BUG_ON(sizeof(*data) != 2 * HEH_BLOCK_SIZE);
+ memcpy(data->nonce, req->info, HEH_BLOCK_SIZE);
+ data->nonce_length = cpu_to_le32(HEH_BLOCK_SIZE);
+ data->aad_length = cpu_to_le32(0);
+ data->message_length = cpu_to_le32(req->nbytes);
+ data->padding = cpu_to_le32(0);
+
+ desc->tfm = ctx->cmac;
+ desc->flags = req->base.flags;
+
+ err = crypto_shash_digest(desc, (const u8 *)data, sizeof(*data),
+ (u8 *)beta1_key);
+ if (err)
+ return err;
+
+ gf128mul_x_ble(beta2_key, beta1_key);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ * This is the generic version of poly_hash. It does the GF(2^128)
+ * multiplication by 'tau_key' using a precomputed table, without using any
+ * special CPU instructions. On some platforms, an accelerated version (with
+ * higher cra_priority) may be used instead.
+ */
+
+struct poly_hash_tfm_ctx {
+ struct gf128mul_4k *tau_key;
+};
+
+struct poly_hash_desc_ctx {
+ be128 digest;
+ unsigned int count;
+};
+
+static int poly_hash_setkey(struct crypto_shash *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(tfm);
+ be128 key128;
+
+ if (keylen != HEH_BLOCK_SIZE) {
+ crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ if (tctx->tau_key)
+ gf128mul_free_4k(tctx->tau_key);
+ memcpy(&key128, key, HEH_BLOCK_SIZE);
+ tctx->tau_key = gf128mul_init_4k_ble(&key128);
+ if (!tctx->tau_key)
+ return -ENOMEM;
+ return 0;
+}
+
+static int poly_hash_init(struct shash_desc *desc)
+{
+ struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
+
+ ctx->digest = (be128) { 0 };
+ ctx->count = 0;
+ return 0;
+}
+
+static int poly_hash_update(struct shash_desc *desc, const u8 *src,
+ unsigned int len)
+{
+ struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
+ struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
+ unsigned int partial = ctx->count % HEH_BLOCK_SIZE;
+ u8 *dst = (u8 *)&ctx->digest + partial;
+
+ ctx->count += len;
+
+ /* Finishing at least one block? */
+ if (partial + len >= HEH_BLOCK_SIZE) {
+
+ if (partial) {
+ /* Finish the pending block. */
+ unsigned int n = HEH_BLOCK_SIZE - partial;
+
+ len -= n;
+ do {
+ *dst++ ^= *src++;
+ } while (--n);
+
+ gf128mul_4k_ble(&ctx->digest, tctx->tau_key);
+ }
+
+ /* Process zero or more full blocks. */
+ while (len >= HEH_BLOCK_SIZE) {
+ be128 coeff;
+
+ memcpy(&coeff, src, HEH_BLOCK_SIZE);
+ be128_xor(&ctx->digest, &ctx->digest, &coeff);
+ src += HEH_BLOCK_SIZE;
+ len -= HEH_BLOCK_SIZE;
+ gf128mul_4k_ble(&ctx->digest, tctx->tau_key);
+ }
+ dst = (u8 *)&ctx->digest;
+ }
+
+ /* Continue adding the next block to 'digest'. */
+ while (len--)
+ *dst++ ^= *src++;
+ return 0;
+}
+
+static int poly_hash_final(struct shash_desc *desc, u8 *out)
+{
+ struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
+
+ /* Finish the last block if needed. */
+ if (ctx->count % HEH_BLOCK_SIZE) {
+ struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
+
+ gf128mul_4k_ble(&ctx->digest, tctx->tau_key);
+ }
+
+ memcpy(out, &ctx->digest, HEH_BLOCK_SIZE);
+ return 0;
+}
+
+static void poly_hash_exit(struct crypto_tfm *tfm)
+{
+ struct poly_hash_tfm_ctx *tctx = crypto_tfm_ctx(tfm);
+
+ gf128mul_free_4k(tctx->tau_key);
+}
+
+static struct shash_alg poly_hash_alg = {
+ .digestsize = HEH_BLOCK_SIZE,
+ .init = poly_hash_init,
+ .update = poly_hash_update,
+ .final = poly_hash_final,
+ .setkey = poly_hash_setkey,
+ .descsize = sizeof(struct poly_hash_desc_ctx),
+ .base = {
+ .cra_name = "poly_hash",
+ .cra_driver_name = "poly_hash-generic",
+ .cra_priority = 100,
+ .cra_ctxsize = sizeof(struct poly_hash_tfm_ctx),
+ .cra_exit = poly_hash_exit,
+ .cra_module = THIS_MODULE,
+ },
+};
+
+/*****************************************************************************/
+
+/*
+ * Split the message into 16 byte blocks, padding out the last block, and use
+ * the blocks as coefficients in the evaluation of a polynomial over GF(2^128)
+ * at the secret point 'tau_key'. For ease of implementing the higher-level
+ * heh_hash_inv() function, the constant and degree-1 coefficients are swapped
+ * if there is a partial block.
+ *
+ * Mathematically, compute:
+ * if (no partial block)
+ * k^{N-1} * m_0 + ... + k * m_{N-2} + m_{N-1}
+ * else if (partial block)
+ * k^N * m_0 + ... + k^2 * m_{N-2} + k * m_N + m_{N-1}
+ *
+ * where:
+ * t is tau_key
+ * N is the number of full blocks in the message
+ * m_i is the i-th full block in the message for i = 0 to N-1 inclusive
+ * m_N is the partial block of the message zero-padded up to 16 bytes
+ *
+ * Note that most of this is now separated out into its own keyed hash
+ * algorithm, to allow optimized implementations. However, we still handle the
+ * swapping of the last two coefficients here in the HEH template because this
+ * simplifies the poly_hash algorithms: they don't have to buffer an extra
+ * block, don't have to duplicate as much code, and are more similar to GHASH.
+ */
+static int poly_hash(struct ablkcipher_request *req, struct scatterlist *sgl,
+ be128 *hash)
+{
+ struct heh_req_ctx *rctx = heh_req_ctx(req);
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct shash_desc *desc = &rctx->u.poly_hash.desc;
+ unsigned int tail_offset = get_tail_offset(req->nbytes);
+ unsigned int tail_len = req->nbytes - tail_offset;
+ be128 tail[2];
+ unsigned int i, n;
+ struct sg_mapping_iter miter;
+ int err;
+
+ desc->tfm = ctx->poly_hash;
+ desc->flags = req->base.flags;
+
+ /* Handle all full blocks except the last */
+ err = crypto_shash_init(desc);
+ sg_miter_start(&miter, sgl, sg_nents(sgl),
+ SG_MITER_FROM_SG | SG_MITER_ATOMIC);
+ for (i = 0; i < tail_offset && !err; i += n) {
+ sg_miter_next(&miter);
+ n = min_t(unsigned int, miter.length, tail_offset - i);
+ err = crypto_shash_update(desc, miter.addr, n);
+ }
+ sg_miter_stop(&miter);
+ if (err)
+ return err;
+
+ /* Handle the last full block and the partial block */
+ scatterwalk_map_and_copy(tail, sgl, tail_offset, tail_len, 0);
+
+ if (tail_len != HEH_BLOCK_SIZE) {
+ /* handle the partial block */
+ memset((u8 *)tail + tail_len, 0, sizeof(tail) - tail_len);
+ err = crypto_shash_update(desc, (u8 *)&tail[1], HEH_BLOCK_SIZE);
+ if (err)
+ return err;
+ }
+ err = crypto_shash_final(desc, (u8 *)hash);
+ if (err)
+ return err;
+ be128_xor(hash, hash, &tail[0]);
+ return 0;
+}
+
+/*
+ * Transform all full blocks except the last.
+ * This is used by both the hash and inverse hash phases.
+ */
+static int heh_tfm_blocks(struct ablkcipher_request *req,
+ struct scatterlist *src_sgl,
+ struct scatterlist *dst_sgl, unsigned int len,
+ const be128 *hash, const be128 *beta_key)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct blkcipher_desc desc = { .flags = req->base.flags };
+ struct blkcipher_walk walk;
+ be128 e = *beta_key;
+ int err;
+ unsigned int nbytes;
+
+ blkcipher_walk_init(&walk, dst_sgl, src_sgl, len);
+
+ err = blkcipher_ablkcipher_walk_virt(&desc, &walk, tfm);
+
+ while ((nbytes = walk.nbytes)) {
+ const be128 *src = (be128 *)walk.src.virt.addr;
+ be128 *dst = (be128 *)walk.dst.virt.addr;
+
+ do {
+ gf128mul_x_ble(&e, &e);
+ be128_xor(dst, src, hash);
+ be128_xor(dst, dst, &e);
+ src++;
+ dst++;
+ } while ((nbytes -= HEH_BLOCK_SIZE) >= HEH_BLOCK_SIZE);
+ err = blkcipher_walk_done(&desc, &walk, nbytes);
+ }
+ return err;
+}
+
+/*
+ * The hash phase of HEH. Given a message, compute:
+ *
+ * (m_0 + H, ..., m_{N-2} + H, H, m_N) + (xb, x^2b, ..., x^{N-1}b, b, 0)
+ *
+ * where:
+ * N is the number of full blocks in the message
+ * m_i is the i-th full block in the message for i = 0 to N-1 inclusive
+ * m_N is the unpadded partial block, possibly empty
+ * H is the poly_hash() of the message, keyed by tau_key
+ * b is beta_key
+ * x is the element x in our representation of GF(2^128)
+ *
+ * Note that the partial block remains unchanged, but it does affect the result
+ * of poly_hash() and therefore the transformation of all the full blocks.
+ */
+static int heh_hash(struct ablkcipher_request *req, const be128 *beta_key)
+{
+ be128 hash;
+ unsigned int tail_offset = get_tail_offset(req->nbytes);
+ unsigned int partial_len = req->nbytes % HEH_BLOCK_SIZE;
+ int err;
+
+ /* poly_hash() the full message including the partial block */
+ err = poly_hash(req, req->src, &hash);
+ if (err)
+ return err;
+
+ /* Transform all full blocks except the last */
+ err = heh_tfm_blocks(req, req->src, req->dst, tail_offset, &hash,
+ beta_key);
+ if (err)
+ return err;
+
+ /* Set the last full block to hash XOR beta_key */
+ be128_xor(&hash, &hash, beta_key);
+ scatterwalk_map_and_copy(&hash, req->dst, tail_offset, HEH_BLOCK_SIZE,
+ 1);
+
+ /* Copy the partial block if needed */
+ if (partial_len != 0 && req->src != req->dst) {
+ unsigned int offs = tail_offset + HEH_BLOCK_SIZE;
+
+ scatterwalk_map_and_copy(&hash, req->src, offs, partial_len, 0);
+ scatterwalk_map_and_copy(&hash, req->dst, offs, partial_len, 1);
+ }
+ return 0;
+}
+
+/*
+ * The inverse hash phase of HEH. This undoes the result of heh_hash().
+ */
+static int heh_hash_inv(struct ablkcipher_request *req, const be128 *beta_key)
+{
+ be128 hash;
+ be128 tmp;
+ struct scatterlist tmp_sgl[2];
+ struct scatterlist *tail_sgl;
+ unsigned int tail_offset = get_tail_offset(req->nbytes);
+ struct scatterlist *sgl = req->dst;
+ int err;
+
+ /*
+ * The last full block was computed as hash XOR beta_key, so XOR it with
+ * beta_key to recover hash.
+ */
+ tail_sgl = scatterwalk_ffwd(tmp_sgl, sgl, tail_offset);
+ scatterwalk_map_and_copy(&hash, tail_sgl, 0, HEH_BLOCK_SIZE, 0);
+ be128_xor(&hash, &hash, beta_key);
+
+ /* Transform all full blocks except the last */
+ err = heh_tfm_blocks(req, sgl, sgl, tail_offset, &hash, beta_key);
+ if (err)
+ return err;
+
+ /*
+ * Recover the last full block. We know 'hash', i.e. the poly_hash() of
+ * the the original message. The last full block was the constant term
+ * of the polynomial. To recover the last full block, temporarily zero
+ * it, compute the poly_hash(), and take the difference from 'hash'.
+ */
+ memset(&tmp, 0, sizeof(tmp));
+ scatterwalk_map_and_copy(&tmp, tail_sgl, 0, HEH_BLOCK_SIZE, 1);
+ err = poly_hash(req, sgl, &tmp);
+ if (err)
+ return err;
+ be128_xor(&tmp, &tmp, &hash);
+ scatterwalk_map_and_copy(&tmp, tail_sgl, 0, HEH_BLOCK_SIZE, 1);
+ return 0;
+}
+
+static int heh_hash_inv_step(struct ablkcipher_request *req, u32 flags)
+{
+ struct heh_req_ctx *rctx = heh_req_ctx(req);
+
+ return heh_hash_inv(req, &rctx->beta2_key);
+}
+
+static int heh_ecb_step_3(struct ablkcipher_request *req, u32 flags)
+{
+ struct heh_req_ctx *rctx = heh_req_ctx(req);
+ u8 partial_block[HEH_BLOCK_SIZE] __aligned(__alignof__(u32));
+ unsigned int tail_offset = get_tail_offset(req->nbytes);
+ unsigned int partial_offset = tail_offset + HEH_BLOCK_SIZE;
+ unsigned int partial_len = req->nbytes - partial_offset;
+
+ /*
+ * Extract the pad in req->dst at tail_offset, and xor the partial block
+ * with it to create encrypted partial block
+ */
+ scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset,
+ HEH_BLOCK_SIZE, 0);
+ scatterwalk_map_and_copy(partial_block, req->dst, partial_offset,
+ partial_len, 0);
+ crypto_xor(partial_block, rctx->u.ecb.keystream, partial_len);
+
+ /*
+ * Store the encrypted final block and partial block back in dst_sg
+ */
+ scatterwalk_map_and_copy(&rctx->u.ecb.tmp, req->dst, tail_offset,
+ HEH_BLOCK_SIZE, 1);
+ scatterwalk_map_and_copy(partial_block, req->dst, partial_offset,
+ partial_len, 1);
+
+ return heh_hash_inv_step(req, flags);
+}
+
+static void heh_ecb_step_2_done(struct crypto_async_request *areq, int err)
+{
+ return async_done(areq, err, heh_ecb_step_3);
+}
+
+static int heh_ecb_step_2(struct ablkcipher_request *req, u32 flags)
+{
+ struct heh_req_ctx *rctx = heh_req_ctx(req);
+ unsigned int partial_len = req->nbytes % HEH_BLOCK_SIZE;
+ struct scatterlist *tmp_sgl;
+ int err;
+ unsigned int tail_offset = get_tail_offset(req->nbytes);
+
+ if (partial_len == 0)
+ return heh_hash_inv_step(req, flags);
+
+ /*
+ * Extract the final full block, store it in tmp, and then xor that with
+ * the value saved in u.ecb.keystream
+ */
+ scatterwalk_map_and_copy(rctx->u.ecb.tmp, req->dst, tail_offset,
+ HEH_BLOCK_SIZE, 0);
+ crypto_xor(rctx->u.ecb.keystream, rctx->u.ecb.tmp, HEH_BLOCK_SIZE);
+
+ /*
+ * Encrypt the value in rctx->u.ecb.keystream to create the pad for the
+ * partial block.
+ * We cannot encrypt stack buffers, so re-use the dst_sg to do this
+ * encryption to avoid a malloc. The value at tail_offset is stored in
+ * tmp, and will be restored later.
+ */
+ scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset,
+ HEH_BLOCK_SIZE, 1);
+ tmp_sgl = scatterwalk_ffwd(rctx->u.ecb.tmp_sgl, req->dst, tail_offset);
+ ablkcipher_request_set_callback(&rctx->u.ecb.req, flags,
+ heh_ecb_step_2_done, req);
+ ablkcipher_request_set_crypt(&rctx->u.ecb.req, tmp_sgl, tmp_sgl,
+ HEH_BLOCK_SIZE, NULL);
+ err = crypto_ablkcipher_encrypt(&rctx->u.ecb.req);
+ if (err)
+ return err;
+ return heh_ecb_step_3(req, flags);
+}
+
+static void heh_ecb_full_done(struct crypto_async_request *areq, int err)
+{
+ return async_done(areq, err, heh_ecb_step_2);
+}
+
+/*
+ * The encrypt phase of HEH. This uses ECB encryption, with special handling
+ * for the partial block at the end if any. The source data is already in
+ * req->dst, so the encryption happens in-place.
+ *
+ * After the encrypt phase we continue on to the inverse hash phase. The
+ * functions calls are chained to support asynchronous ECB algorithms.
+ */
+static int heh_ecb(struct ablkcipher_request *req, bool decrypt)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct heh_req_ctx *rctx = heh_req_ctx(req);
+ struct ablkcipher_request *ecb_req = &rctx->u.ecb.req;
+ unsigned int tail_offset = get_tail_offset(req->nbytes);
+ unsigned int full_len = tail_offset + HEH_BLOCK_SIZE;
+ int err;
+
+ /*
+ * Save the last full block before it is encrypted/decrypted. This will
+ * be used later to encrypt/decrypt the partial block
+ */
+ scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset,
+ HEH_BLOCK_SIZE, 0);
+
+ /* Encrypt/decrypt all full blocks */
+ ablkcipher_request_set_tfm(ecb_req, ctx->ecb);
+ ablkcipher_request_set_callback(ecb_req, req->base.flags,
+ heh_ecb_full_done, req);
+ ablkcipher_request_set_crypt(ecb_req, req->dst, req->dst, full_len,
+ NULL);
+ if (decrypt)
+ err = crypto_ablkcipher_decrypt(ecb_req);
+ else
+ err = crypto_ablkcipher_encrypt(ecb_req);
+ if (err)
+ return err;
+
+ return heh_ecb_step_2(req, req->base.flags);
+}
+
+static int heh_crypt(struct ablkcipher_request *req, bool decrypt)
+{
+ struct heh_req_ctx *rctx = heh_req_ctx(req);
+ int err;
+
+ /* Inputs must be at least one full block */
+ if (req->nbytes < HEH_BLOCK_SIZE)
+ return -EINVAL;
+
+ err = generate_betas(req, &rctx->beta1_key, &rctx->beta2_key);
+ if (err)
+ return err;
+
+ if (decrypt)
+ swap(rctx->beta1_key, rctx->beta2_key);
+
+ err = heh_hash(req, &rctx->beta1_key);
+ if (err)
+ return err;
+
+ return heh_ecb(req, decrypt);
+}
+
+static int heh_encrypt(struct ablkcipher_request *req)
+{
+ return heh_crypt(req, false);
+}
+
+static int heh_decrypt(struct ablkcipher_request *req)
+{
+ return heh_crypt(req, true);
+}
+
+static int heh_setkey(struct crypto_ablkcipher *parent, const u8 *key,
+ unsigned int keylen)
+{
+ struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(parent);
+ struct crypto_shash *cmac = ctx->cmac;
+ struct crypto_ablkcipher *ecb = ctx->ecb;
+ SHASH_DESC_ON_STACK(desc, cmac);
+ u8 *derived_keys;
+ u8 digest[HEH_BLOCK_SIZE];
+ unsigned int i;
+ int err;
+
+ /* set prf_key = key */
+ crypto_shash_clear_flags(cmac, CRYPTO_TFM_REQ_MASK);
+ crypto_shash_set_flags(cmac, crypto_ablkcipher_get_flags(parent) &
+ CRYPTO_TFM_REQ_MASK);
+ err = crypto_shash_setkey(cmac, key, keylen);
+ crypto_ablkcipher_set_flags(parent, crypto_shash_get_flags(cmac) &
+ CRYPTO_TFM_RES_MASK);
+ if (err)
+ return err;
+
+ /*
+ * Generate tau_key and ecb_key as follows:
+ * tau_key = cmac(prf_key, 0x00...01)
+ * ecb_key = cmac(prf_key, 0x00...02) || cmac(prf_key, 0x00...03) || ...
+ * truncated to keylen bytes
+ */
+ derived_keys = kzalloc(round_up(HEH_BLOCK_SIZE + keylen,
+ HEH_BLOCK_SIZE), GFP_KERNEL);
+ if (!derived_keys)
+ return -ENOMEM;
+ desc->tfm = cmac;
+ desc->flags = (crypto_shash_get_flags(cmac) & CRYPTO_TFM_REQ_MASK);
+ for (i = 0; i < keylen + HEH_BLOCK_SIZE; i += HEH_BLOCK_SIZE) {
+ derived_keys[i + HEH_BLOCK_SIZE - 1] =
+ 0x01 + i / HEH_BLOCK_SIZE;
+ err = crypto_shash_digest(desc, derived_keys + i,
+ HEH_BLOCK_SIZE, digest);
+ if (err)
+ goto out;
+ memcpy(derived_keys + i, digest, HEH_BLOCK_SIZE);
+ }
+
+ err = crypto_shash_setkey(ctx->poly_hash, derived_keys, HEH_BLOCK_SIZE);
+ if (err)
+ goto out;
+
+ crypto_ablkcipher_clear_flags(ecb, CRYPTO_TFM_REQ_MASK);
+ crypto_ablkcipher_set_flags(ecb, crypto_ablkcipher_get_flags(parent) &
+ CRYPTO_TFM_REQ_MASK);
+ err = crypto_ablkcipher_setkey(ecb, derived_keys + HEH_BLOCK_SIZE,
+ keylen);
+ crypto_ablkcipher_set_flags(parent, crypto_ablkcipher_get_flags(ecb) &
+ CRYPTO_TFM_RES_MASK);
+out:
+ kzfree(derived_keys);
+ return err;
+}
+
+static int heh_init_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+ struct heh_instance_ctx *ictx = crypto_instance_ctx(inst);
+ struct heh_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_shash *cmac;
+ struct crypto_shash *poly_hash;
+ struct crypto_ablkcipher *ecb;
+ unsigned int reqsize;
+ int err;
+
+ cmac = crypto_spawn_shash(&ictx->cmac);
+ if (IS_ERR(cmac))
+ return PTR_ERR(cmac);
+
+ poly_hash = crypto_spawn_shash(&ictx->poly_hash);
+ err = PTR_ERR(poly_hash);
+ if (IS_ERR(poly_hash))
+ goto err_free_cmac;
+
+ ecb = crypto_spawn_skcipher(&ictx->ecb);
+ err = PTR_ERR(ecb);
+ if (IS_ERR(ecb))
+ goto err_free_poly_hash;
+
+ ctx->cmac = cmac;
+ ctx->poly_hash = poly_hash;
+ ctx->ecb = ecb;
+
+ reqsize = crypto_tfm_alg_alignmask(tfm) &
+ ~(crypto_tfm_ctx_alignment() - 1);
+ reqsize += max3(offsetof(struct heh_req_ctx, u.cmac.desc) +
+ sizeof(struct shash_desc) +
+ crypto_shash_descsize(cmac),
+ offsetof(struct heh_req_ctx, u.poly_hash.desc) +
+ sizeof(struct shash_desc) +
+ crypto_shash_descsize(poly_hash),
+ offsetof(struct heh_req_ctx, u.ecb.req) +
+ sizeof(struct ablkcipher_request) +
+ crypto_ablkcipher_reqsize(ecb));
+ tfm->crt_ablkcipher.reqsize = reqsize;
+
+ return 0;
+
+err_free_poly_hash:
+ crypto_free_shash(poly_hash);
+err_free_cmac:
+ crypto_free_shash(cmac);
+ return err;
+}
+
+static void heh_exit_tfm(struct crypto_tfm *tfm)
+{
+ struct heh_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_shash(ctx->cmac);
+ crypto_free_shash(ctx->poly_hash);
+ crypto_free_ablkcipher(ctx->ecb);
+}
+
+static void heh_free_instance(struct crypto_instance *inst)
+{
+ struct heh_instance_ctx *ctx = crypto_instance_ctx(inst);
+
+ crypto_drop_shash(&ctx->cmac);
+ crypto_drop_shash(&ctx->poly_hash);
+ crypto_drop_skcipher(&ctx->ecb);
+ kfree(inst);
+}
+
+/*
+ * Create an instance of HEH as a ablkcipher.
+ *
+ * This relies on underlying CMAC and ECB algorithms, usually cmac(aes) and
+ * ecb(aes). For performance reasons we support asynchronous ECB algorithms.
+ * However, we do not yet support asynchronous CMAC algorithms because CMAC is
+ * only used on a small fixed amount of data per request, independent of the
+ * request length. This would change if AEAD or variable-length nonce support
+ * were to be exposed.
+ */
+static int heh_create_common(struct crypto_template *tmpl, struct rtattr **tb,
+ const char *full_name, const char *cmac_name,
+ const char *poly_hash_name, const char *ecb_name)
+{
+ struct crypto_attr_type *algt;
+ struct crypto_instance *inst;
+ struct heh_instance_ctx *ctx;
+ struct shash_alg *cmac;
+ struct shash_alg *poly_hash;
+ struct crypto_alg *ecb;
+ int err;
+
+ algt = crypto_get_attr_type(tb);
+ if (IS_ERR(algt))
+ return PTR_ERR(algt);
+
+ /* User must be asking for something compatible with ablkcipher */
+ if ((algt->type ^ CRYPTO_ALG_TYPE_ABLKCIPHER) & algt->mask)
+ return -EINVAL;
+
+ /* Allocate the ablkcipher instance */
+ inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ ctx = crypto_instance_ctx(inst);
+
+ /* Set up the cmac spawn */
+ ctx->cmac.base.inst = inst;
+ err = crypto_grab_shash(&ctx->cmac, cmac_name, 0, 0);
+ if (err)
+ goto err_free_inst;
+ cmac = crypto_spawn_shash_alg(&ctx->cmac);
+ err = -EINVAL;
+ if (cmac->digestsize != HEH_BLOCK_SIZE)
+ goto err_drop_cmac;
+
+ /* Set up the poly_hash spawn */
+ ctx->poly_hash.base.inst = inst;
+ err = crypto_grab_shash(&ctx->poly_hash, poly_hash_name, 0, 0);
+ if (err)
+ goto err_drop_cmac;
+ poly_hash = crypto_spawn_shash_alg(&ctx->poly_hash);
+ err = -EINVAL;
+ if (poly_hash->digestsize != HEH_BLOCK_SIZE)
+ goto err_drop_poly_hash;
+
+ /* Set up the ecb spawn */
+ ctx->ecb.base.inst = inst;
+ err = crypto_grab_skcipher(&ctx->ecb, ecb_name, 0,
+ crypto_requires_sync(algt->type,
+ algt->mask));
+ if (err)
+ goto err_drop_poly_hash;
+ ecb = crypto_skcipher_spawn_alg(&ctx->ecb);
+
+ /* HEH only supports block ciphers with 16 byte block size */
+ err = -EINVAL;
+ if (ecb->cra_blocksize != HEH_BLOCK_SIZE)
+ goto err_drop_ecb;
+
+ /* The underlying "ECB" algorithm must not require an IV */
+ err = -EINVAL;
+ if ((ecb->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_BLKCIPHER) {
+ if (ecb->cra_blkcipher.ivsize != 0)
+ goto err_drop_ecb;
+ } else {
+ if (ecb->cra_ablkcipher.ivsize != 0)
+ goto err_drop_ecb;
+ }
+
+ /* Set the instance names */
+ err = -ENAMETOOLONG;
+ if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+ "heh_base(%s,%s,%s)", cmac->base.cra_driver_name,
+ poly_hash->base.cra_driver_name,
+ ecb->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
+ goto err_drop_ecb;
+
+ err = -ENAMETOOLONG;
+ if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
+ "%s", full_name) >= CRYPTO_MAX_ALG_NAME)
+ goto err_drop_ecb;
+
+ /* Finish initializing the instance */
+
+ inst->alg.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ (ecb->cra_flags & CRYPTO_ALG_ASYNC);
+ inst->alg.cra_blocksize = HEH_BLOCK_SIZE;
+ inst->alg.cra_ctxsize = sizeof(struct heh_tfm_ctx);
+ inst->alg.cra_alignmask = ecb->cra_alignmask | (__alignof__(be128) - 1);
+ inst->alg.cra_priority = ecb->cra_priority;
+ inst->alg.cra_type = &crypto_ablkcipher_type;
+ inst->alg.cra_init = heh_init_tfm;
+ inst->alg.cra_exit = heh_exit_tfm;
+
+ inst->alg.cra_ablkcipher.setkey = heh_setkey;
+ inst->alg.cra_ablkcipher.encrypt = heh_encrypt;
+ inst->alg.cra_ablkcipher.decrypt = heh_decrypt;
+ if ((ecb->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_BLKCIPHER) {
+ inst->alg.cra_ablkcipher.min_keysize = ecb->cra_blkcipher.min_keysize;
+ inst->alg.cra_ablkcipher.max_keysize = ecb->cra_blkcipher.max_keysize;
+ } else {
+ inst->alg.cra_ablkcipher.min_keysize = ecb->cra_ablkcipher.min_keysize;
+ inst->alg.cra_ablkcipher.max_keysize = ecb->cra_ablkcipher.max_keysize;
+ }
+ inst->alg.cra_ablkcipher.ivsize = HEH_BLOCK_SIZE;
+
+ /* Register the instance */
+ err = crypto_register_instance(tmpl, inst);
+ if (err)
+ goto err_drop_ecb;
+ return 0;
+
+err_drop_ecb:
+ crypto_drop_skcipher(&ctx->ecb);
+err_drop_poly_hash:
+ crypto_drop_shash(&ctx->poly_hash);
+err_drop_cmac:
+ crypto_drop_shash(&ctx->cmac);
+err_free_inst:
+ kfree(inst);
+ return err;
+}
+
+static int heh_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+ const char *cipher_name;
+ char full_name[CRYPTO_MAX_ALG_NAME];
+ char cmac_name[CRYPTO_MAX_ALG_NAME];
+ char ecb_name[CRYPTO_MAX_ALG_NAME];
+
+ /* Get the name of the requested block cipher (e.g. aes) */
+ cipher_name = crypto_attr_alg_name(tb[1]);
+ if (IS_ERR(cipher_name))
+ return PTR_ERR(cipher_name);
+
+ if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "heh(%s)", cipher_name) >=
+ CRYPTO_MAX_ALG_NAME)
+ return -ENAMETOOLONG;
+
+ if (snprintf(cmac_name, CRYPTO_MAX_ALG_NAME, "cmac(%s)", cipher_name) >=
+ CRYPTO_MAX_ALG_NAME)
+ return -ENAMETOOLONG;
+
+ if (snprintf(ecb_name, CRYPTO_MAX_ALG_NAME, "ecb(%s)", cipher_name) >=
+ CRYPTO_MAX_ALG_NAME)
+ return -ENAMETOOLONG;
+
+ return heh_create_common(tmpl, tb, full_name, cmac_name, "poly_hash",
+ ecb_name);
+}
+
+static struct crypto_template heh_tmpl = {
+ .name = "heh",
+ .create = heh_create,
+ .free = heh_free_instance,
+ .module = THIS_MODULE,
+};
+
+static int heh_base_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+ char full_name[CRYPTO_MAX_ALG_NAME];
+ const char *cmac_name;
+ const char *poly_hash_name;
+ const char *ecb_name;
+
+ cmac_name = crypto_attr_alg_name(tb[1]);
+ if (IS_ERR(cmac_name))
+ return PTR_ERR(cmac_name);
+
+ poly_hash_name = crypto_attr_alg_name(tb[2]);
+ if (IS_ERR(poly_hash_name))
+ return PTR_ERR(poly_hash_name);
+
+ ecb_name = crypto_attr_alg_name(tb[3]);
+ if (IS_ERR(ecb_name))
+ return PTR_ERR(ecb_name);
+
+ if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "heh_base(%s,%s,%s)",
+ cmac_name, poly_hash_name, ecb_name) >=
+ CRYPTO_MAX_ALG_NAME)
+ return -ENAMETOOLONG;
+
+ return heh_create_common(tmpl, tb, full_name, cmac_name, poly_hash_name,
+ ecb_name);
+}
+
+/*
+ * If HEH is instantiated as "heh_base" instead of "heh", then specific
+ * implementations of cmac, poly_hash, and ecb can be specified instead of just
+ * the cipher.
+ */
+static struct crypto_template heh_base_tmpl = {
+ .name = "heh_base",
+ .create = heh_base_create,
+ .free = heh_free_instance,
+ .module = THIS_MODULE,
+};
+
+static int __init heh_module_init(void)
+{
+ int err;
+
+ err = crypto_register_template(&heh_tmpl);
+ if (err)
+ return err;
+
+ err = crypto_register_template(&heh_base_tmpl);
+ if (err)
+ goto out_undo_heh;
+
+ err = crypto_register_shash(&poly_hash_alg);
+ if (err)
+ goto out_undo_heh_base;
+
+ return 0;
+
+out_undo_heh_base:
+ crypto_unregister_template(&heh_base_tmpl);
+out_undo_heh:
+ crypto_unregister_template(&heh_tmpl);
+ return err;
+}
+
+static void __exit heh_module_exit(void)
+{
+ crypto_unregister_template(&heh_tmpl);
+ crypto_unregister_template(&heh_base_tmpl);
+ crypto_unregister_shash(&poly_hash_alg);
+}
+
+module_init(heh_module_init);
+module_exit(heh_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Hash-Encrypt-Hash block cipher mode");
+MODULE_ALIAS_CRYPTO("heh");
+MODULE_ALIAS_CRYPTO("heh_base");
diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c
index a0ceb41d5ccc..b4f3930266b1 100644
--- a/crypto/mcryptd.c
+++ b/crypto/mcryptd.c
@@ -531,6 +531,7 @@ static int mcryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb,
inst->alg.halg.base.cra_flags = type;
inst->alg.halg.digestsize = salg->digestsize;
+ inst->alg.halg.statesize = salg->statesize;
inst->alg.halg.base.cra_ctxsize = sizeof(struct mcryptd_hash_ctx);
inst->alg.halg.base.cra_init = mcryptd_hash_init_tfm;
diff --git a/crypto/shash.c b/crypto/shash.c
index 359754591653..9ae1e891308d 100644
--- a/crypto/shash.c
+++ b/crypto/shash.c
@@ -683,6 +683,14 @@ void shash_free_instance(struct crypto_instance *inst)
}
EXPORT_SYMBOL_GPL(shash_free_instance);
+int crypto_grab_shash(struct crypto_shash_spawn *spawn,
+ const char *name, u32 type, u32 mask)
+{
+ spawn->base.frontend = &crypto_shash_type;
+ return crypto_grab_spawn(&spawn->base, name, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_grab_shash);
+
int crypto_init_shash_spawn(struct crypto_shash_spawn *spawn,
struct shash_alg *alg,
struct crypto_instance *inst)
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index d4944318ca1f..8374ca8b6579 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -3214,6 +3214,21 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}
}, {
+ .alg = "heh(aes)",
+ .test = alg_test_skcipher,
+ .suite = {
+ .cipher = {
+ .enc = {
+ .vecs = aes_heh_enc_tv_template,
+ .count = AES_HEH_ENC_TEST_VECTORS
+ },
+ .dec = {
+ .vecs = aes_heh_dec_tv_template,
+ .count = AES_HEH_DEC_TEST_VECTORS
+ }
+ }
+ }
+ }, {
.alg = "hmac(crc32)",
.test = alg_test_hash,
.suite = {
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 0e02c60a57b6..ba6530d8ba58 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -14139,6 +14139,8 @@ static struct cipher_testvec cast6_xts_dec_tv_template[] = {
#define AES_DEC_TEST_VECTORS 4
#define AES_CBC_ENC_TEST_VECTORS 5
#define AES_CBC_DEC_TEST_VECTORS 5
+#define AES_HEH_ENC_TEST_VECTORS 4
+#define AES_HEH_DEC_TEST_VECTORS 4
#define HMAC_MD5_ECB_CIPHER_NULL_ENC_TEST_VECTORS 2
#define HMAC_MD5_ECB_CIPHER_NULL_DEC_TEST_VECTORS 2
#define HMAC_SHA1_ECB_CIPHER_NULL_ENC_TEST_VEC 2
@@ -14511,6 +14513,198 @@ static struct cipher_testvec aes_dec_tv_template[] = {
},
};
+static struct cipher_testvec aes_heh_enc_tv_template[] = {
+ {
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ .klen = 16,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ .ilen = 16,
+ .result = "\xd8\xbd\x40\xbf\xca\xe5\xee\x81"
+ "\x0f\x3d\x1f\x1f\xae\x89\x07\x55",
+ .rlen = 16,
+ .also_non_np = 1,
+ .np = 2,
+ .tap = { 8, 8 },
+ }, {
+ .key = "\xa8\xda\x24\x9b\x5e\xfa\x13\xc2"
+ "\xc1\x94\xbf\x32\xba\x38\xa3\x77",
+ .klen = 16,
+ .iv = "\x4d\x47\x61\x37\x2b\x47\x86\xf0"
+ "\xd6\x47\xb5\xc2\xe8\xcf\x85\x27",
+ .input = "\xb8\xee\x29\xe4\xa5\xd1\xe7\x55"
+ "\xd0\xfd\xe7\x22\x63\x76\x36\xe2"
+ "\xf8\x0c\xf8\xfe\x65\x76\xe7\xca"
+ "\xc1\x42\xf5\xca\x5a\xa8\xac\x2a",
+ .ilen = 32,
+ .result = "\x59\xf2\x78\x4e\x10\x94\xf9\x5c"
+ "\x22\x23\x78\x2a\x30\x48\x11\x97"
+ "\xb1\xfe\x70\xc4\xef\xdf\x04\xef"
+ "\x16\x39\x04\xcf\xc0\x95\x9a\x98",
+ .rlen = 32,
+ .also_non_np = 1,
+ .np = 3,
+ .tap = { 16, 13, 3 },
+ }, {
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ .klen = 16,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00",
+ .ilen = 63,
+ .result = "\xe0\x40\xeb\xe9\x52\xbe\x65\x60"
+ "\xe4\x68\x68\xa3\x73\x75\xb8\x52"
+ "\xef\x38\x6a\x87\x25\x25\xf6\x04"
+ "\xe5\x8e\xbe\x14\x8b\x02\x14\x1f"
+ "\xa9\x73\xb7\xad\x15\xbe\x9c\xa0"
+ "\xd2\x8a\x2c\xdc\xd4\xe3\x05\x55"
+ "\x0a\xf5\xf8\x51\xee\xe5\x62\xa5"
+ "\x71\xa7\x7c\x15\x5d\x7a\x9e",
+ .rlen = 63,
+ .also_non_np = 1,
+ .np = 8,
+ .tap = { 20, 20, 10, 8, 2, 1, 1, 1 },
+ }, {
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ .klen = 16,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x01"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00",
+ .ilen = 63,
+ .result = "\x4b\x1a\x15\xa0\xaf\x08\x6d\x70"
+ "\xf0\xa7\x97\xb5\x31\x4b\x8c\xc3"
+ "\x4d\xf2\x7a\x9d\xdd\xd4\x15\x99"
+ "\x57\xad\xc6\xb1\x35\x69\xf5\x6a"
+ "\x2d\x70\xe4\x97\x49\xb2\x9f\x71"
+ "\xde\x22\xb5\x70\x8c\x69\x24\xd3"
+ "\xad\x80\x58\x48\x90\xe4\xed\xba"
+ "\x76\x3d\x71\x7c\x57\x25\x87",
+ .rlen = 63,
+ .also_non_np = 1,
+ .np = 8,
+ .tap = { 20, 20, 10, 8, 2, 1, 1, 1 },
+ }
+};
+
+static struct cipher_testvec aes_heh_dec_tv_template[] = {
+ {
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ .klen = 16,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\xd8\xbd\x40\xbf\xca\xe5\xee\x81"
+ "\x0f\x3d\x1f\x1f\xae\x89\x07\x55",
+ .ilen = 16,
+ .result = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ .rlen = 16,
+ .also_non_np = 1,
+ .np = 2,
+ .tap = { 8, 8 },
+ }, {
+ .key = "\xa8\xda\x24\x9b\x5e\xfa\x13\xc2"
+ "\xc1\x94\xbf\x32\xba\x38\xa3\x77",
+ .klen = 16,
+ .iv = "\x4d\x47\x61\x37\x2b\x47\x86\xf0"
+ "\xd6\x47\xb5\xc2\xe8\xcf\x85\x27",
+ .input = "\x59\xf2\x78\x4e\x10\x94\xf9\x5c"
+ "\x22\x23\x78\x2a\x30\x48\x11\x97"
+ "\xb1\xfe\x70\xc4\xef\xdf\x04\xef"
+ "\x16\x39\x04\xcf\xc0\x95\x9a\x98",
+ .ilen = 32,
+ .result = "\xb8\xee\x29\xe4\xa5\xd1\xe7\x55"
+ "\xd0\xfd\xe7\x22\x63\x76\x36\xe2"
+ "\xf8\x0c\xf8\xfe\x65\x76\xe7\xca"
+ "\xc1\x42\xf5\xca\x5a\xa8\xac\x2a",
+ .rlen = 32,
+ .also_non_np = 1,
+ .np = 3,
+ .tap = { 16, 13, 3 },
+ }, {
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ .klen = 16,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\xe0\x40\xeb\xe9\x52\xbe\x65\x60"
+ "\xe4\x68\x68\xa3\x73\x75\xb8\x52"
+ "\xef\x38\x6a\x87\x25\x25\xf6\x04"
+ "\xe5\x8e\xbe\x14\x8b\x02\x14\x1f"
+ "\xa9\x73\xb7\xad\x15\xbe\x9c\xa0"
+ "\xd2\x8a\x2c\xdc\xd4\xe3\x05\x55"
+ "\x0a\xf5\xf8\x51\xee\xe5\x62\xa5"
+ "\x71\xa7\x7c\x15\x5d\x7a\x9e",
+ .ilen = 63,
+ .result = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00",
+ .rlen = 63,
+ .also_non_np = 1,
+ .np = 8,
+ .tap = { 20, 20, 10, 8, 2, 1, 1, 1 },
+ }, {
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ .klen = 16,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\x4b\x1a\x15\xa0\xaf\x08\x6d\x70"
+ "\xf0\xa7\x97\xb5\x31\x4b\x8c\xc3"
+ "\x4d\xf2\x7a\x9d\xdd\xd4\x15\x99"
+ "\x57\xad\xc6\xb1\x35\x69\xf5\x6a"
+ "\x2d\x70\xe4\x97\x49\xb2\x9f\x71"
+ "\xde\x22\xb5\x70\x8c\x69\x24\xd3"
+ "\xad\x80\x58\x48\x90\xe4\xed\xba"
+ "\x76\x3d\x71\x7c\x57\x25\x87",
+ .ilen = 63,
+ .result = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x01"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00",
+ .rlen = 63,
+ .also_non_np = 1,
+ .np = 8,
+ .tap = { 20, 20, 10, 8, 2, 1, 1, 1 },
+ }
+};
+
static struct cipher_testvec aes_cbc_enc_tv_template[] = {
{ /* From RFC 3602 */
.key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b"
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
index 5fdac394207a..549cdbed7b0e 100644
--- a/drivers/acpi/acpi_video.c
+++ b/drivers/acpi/acpi_video.c
@@ -1211,6 +1211,9 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video)
union acpi_object *dod = NULL;
union acpi_object *obj;
+ if (!video->cap._DOD)
+ return AE_NOT_EXIST;
+
status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
if (!ACPI_SUCCESS(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c
index 96809cd99ace..2f24b578bcaf 100644
--- a/drivers/acpi/blacklist.c
+++ b/drivers/acpi/blacklist.c
@@ -346,6 +346,34 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"),
},
},
+ {
+ .callback = dmi_enable_rev_override,
+ .ident = "DELL Precision 5520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Precision 5520"),
+ },
+ },
+ {
+ .callback = dmi_enable_rev_override,
+ .ident = "DELL Precision 3520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3520"),
+ },
+ },
+ /*
+ * Resolves a quirk with the Dell Latitude 3350 that
+ * causes the ethernet adapter to not function.
+ */
+ {
+ .callback = dmi_enable_rev_override,
+ .ident = "DELL Latitude 3350",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude 3350"),
+ },
+ },
#endif
{}
};
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig
index a82fc022d34b..4d4cdc1a6e25 100644
--- a/drivers/android/Kconfig
+++ b/drivers/android/Kconfig
@@ -22,7 +22,7 @@ config ANDROID_BINDER_IPC
config ANDROID_BINDER_DEVICES
string "Android Binder devices"
depends on ANDROID_BINDER_IPC
- default "binder"
+ default "binder,hwbinder,vndbinder"
---help---
Default value for the binder.devices parameter.
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 37b9eecf5c71..d1490be45c67 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -18,6 +18,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/cacheflush.h>
+#include <linux/atomic.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/freezer.h>
@@ -46,19 +47,11 @@
#include <uapi/linux/android/binder.h>
#include "binder_trace.h"
-static DEFINE_MUTEX(binder_main_lock);
-static DEFINE_MUTEX(binder_deferred_lock);
-static DEFINE_MUTEX(binder_mmap_lock);
-
static HLIST_HEAD(binder_devices);
-static HLIST_HEAD(binder_procs);
-static HLIST_HEAD(binder_deferred_list);
-static HLIST_HEAD(binder_dead_nodes);
static struct dentry *binder_debugfs_dir_entry_root;
static struct dentry *binder_debugfs_dir_entry_proc;
-static int binder_last_id;
-static struct workqueue_struct *binder_deferred_workqueue;
+atomic_t binder_last_id;
#define BINDER_DEBUG_ENTRY(name) \
static int binder_##name##_open(struct inode *inode, struct file *file) \
@@ -173,20 +166,24 @@ enum binder_stat_types {
struct binder_stats {
int br[_IOC_NR(BR_FAILED_REPLY) + 1];
int bc[_IOC_NR(BC_REPLY_SG) + 1];
- int obj_created[BINDER_STAT_COUNT];
- int obj_deleted[BINDER_STAT_COUNT];
};
-static struct binder_stats binder_stats;
+/* These are still global, since it's not always easy to get the context */
+struct binder_obj_stats {
+ atomic_t obj_created[BINDER_STAT_COUNT];
+ atomic_t obj_deleted[BINDER_STAT_COUNT];
+};
+
+static struct binder_obj_stats binder_obj_stats;
static inline void binder_stats_deleted(enum binder_stat_types type)
{
- binder_stats.obj_deleted[type]++;
+ atomic_inc(&binder_obj_stats.obj_deleted[type]);
}
static inline void binder_stats_created(enum binder_stat_types type)
{
- binder_stats.obj_created[type]++;
+ atomic_inc(&binder_obj_stats.obj_created[type]);
}
struct binder_transaction_log_entry {
@@ -207,8 +204,6 @@ struct binder_transaction_log {
int full;
struct binder_transaction_log_entry entry[32];
};
-static struct binder_transaction_log binder_transaction_log;
-static struct binder_transaction_log binder_transaction_log_failed;
static struct binder_transaction_log_entry *binder_transaction_log_add(
struct binder_transaction_log *log)
@@ -229,6 +224,21 @@ struct binder_context {
struct binder_node *binder_context_mgr_node;
kuid_t binder_context_mgr_uid;
const char *name;
+
+ struct mutex binder_main_lock;
+ struct mutex binder_deferred_lock;
+ struct mutex binder_mmap_lock;
+
+ struct hlist_head binder_procs;
+ struct hlist_head binder_dead_nodes;
+ struct hlist_head binder_deferred_list;
+
+ struct work_struct deferred_work;
+ struct workqueue_struct *binder_deferred_workqueue;
+ struct binder_transaction_log transaction_log;
+ struct binder_transaction_log transaction_log_failed;
+
+ struct binder_stats binder_stats;
};
struct binder_device {
@@ -459,18 +469,19 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd)
return retval;
}
-static inline void binder_lock(const char *tag)
+static inline void binder_lock(struct binder_context *context, const char *tag)
{
trace_binder_lock(tag);
- mutex_lock(&binder_main_lock);
+ mutex_lock(&context->binder_main_lock);
preempt_disable();
trace_binder_locked(tag);
}
-static inline void binder_unlock(const char *tag)
+static inline void binder_unlock(struct binder_context *context,
+ const char *tag)
{
trace_binder_unlock(tag);
- mutex_unlock(&binder_main_lock);
+ mutex_unlock(&context->binder_main_lock);
preempt_enable();
}
@@ -1017,7 +1028,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
binder_stats_created(BINDER_STAT_NODE);
rb_link_node(&node->rb_node, parent, p);
rb_insert_color(&node->rb_node, &proc->nodes);
- node->debug_id = ++binder_last_id;
+ node->debug_id = atomic_inc_return(&binder_last_id);
node->proc = proc;
node->ptr = ptr;
node->cookie = cookie;
@@ -1159,7 +1170,7 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
if (new_ref == NULL)
return NULL;
binder_stats_created(BINDER_STAT_REF);
- new_ref->debug_id = ++binder_last_id;
+ new_ref->debug_id = atomic_inc_return(&binder_last_id);
new_ref->proc = proc;
new_ref->node = node;
rb_link_node(&new_ref->rb_node_node, parent, p);
@@ -1920,7 +1931,7 @@ static void binder_transaction(struct binder_proc *proc,
binder_size_t last_fixup_min_off = 0;
struct binder_context *context = proc->context;
- e = binder_transaction_log_add(&binder_transaction_log);
+ e = binder_transaction_log_add(&context->transaction_log);
e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
e->from_proc = proc->pid;
e->from_thread = thread->pid;
@@ -2042,7 +2053,7 @@ static void binder_transaction(struct binder_proc *proc,
}
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
- t->debug_id = ++binder_last_id;
+ t->debug_id = atomic_inc_return(&binder_last_id);
e->debug_id = t->debug_id;
if (reply)
@@ -2315,7 +2326,8 @@ err_no_context_mgr_node:
{
struct binder_transaction_log_entry *fe;
- fe = binder_transaction_log_add(&binder_transaction_log_failed);
+ fe = binder_transaction_log_add(
+ &context->transaction_log_failed);
*fe = *e;
}
@@ -2343,8 +2355,8 @@ static int binder_thread_write(struct binder_proc *proc,
return -EFAULT;
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
- if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
- binder_stats.bc[_IOC_NR(cmd)]++;
+ if (_IOC_NR(cmd) < ARRAY_SIZE(context->binder_stats.bc)) {
+ context->binder_stats.bc[_IOC_NR(cmd)]++;
proc->stats.bc[_IOC_NR(cmd)]++;
thread->stats.bc[_IOC_NR(cmd)]++;
}
@@ -2710,8 +2722,8 @@ static void binder_stat_br(struct binder_proc *proc,
struct binder_thread *thread, uint32_t cmd)
{
trace_binder_return(cmd);
- if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) {
- binder_stats.br[_IOC_NR(cmd)]++;
+ if (_IOC_NR(cmd) < ARRAY_SIZE(proc->stats.br)) {
+ proc->context->binder_stats.br[_IOC_NR(cmd)]++;
proc->stats.br[_IOC_NR(cmd)]++;
thread->stats.br[_IOC_NR(cmd)]++;
}
@@ -2775,7 +2787,7 @@ retry:
if (wait_for_proc_work)
proc->ready_threads++;
- binder_unlock(__func__);
+ binder_unlock(proc->context, __func__);
trace_binder_wait_for_work(wait_for_proc_work,
!!thread->transaction_stack,
@@ -2802,7 +2814,7 @@ retry:
ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
}
- binder_lock(__func__);
+ binder_lock(proc->context, __func__);
if (wait_for_proc_work)
proc->ready_threads--;
@@ -3188,14 +3200,14 @@ static unsigned int binder_poll(struct file *filp,
struct binder_thread *thread = NULL;
int wait_for_proc_work;
- binder_lock(__func__);
+ binder_lock(proc->context, __func__);
thread = binder_get_thread(proc);
wait_for_proc_work = thread->transaction_stack == NULL &&
list_empty(&thread->todo) && thread->return_error == BR_OK;
- binder_unlock(__func__);
+ binder_unlock(proc->context, __func__);
if (wait_for_proc_work) {
if (binder_has_proc_work(proc, thread))
@@ -3322,6 +3334,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
+ struct binder_context *context = proc->context;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
@@ -3335,7 +3348,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (ret)
goto err_unlocked;
- binder_lock(__func__);
+ binder_lock(context, __func__);
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
@@ -3386,7 +3399,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err:
if (thread)
thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
- binder_unlock(__func__);
+ binder_unlock(context, __func__);
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
@@ -3459,7 +3472,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
}
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
- mutex_lock(&binder_mmap_lock);
+ mutex_lock(&proc->context->binder_mmap_lock);
if (proc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
@@ -3474,7 +3487,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
}
proc->buffer = area->addr;
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
- mutex_unlock(&binder_mmap_lock);
+ mutex_unlock(&proc->context->binder_mmap_lock);
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
@@ -3523,12 +3536,12 @@ err_alloc_small_buf_failed:
kfree(proc->pages);
proc->pages = NULL;
err_alloc_pages_failed:
- mutex_lock(&binder_mmap_lock);
+ mutex_lock(&proc->context->binder_mmap_lock);
vfree(proc->buffer);
proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
- mutex_unlock(&binder_mmap_lock);
+ mutex_unlock(&proc->context->binder_mmap_lock);
err_bad_arg:
pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
@@ -3555,15 +3568,15 @@ static int binder_open(struct inode *nodp, struct file *filp)
miscdev);
proc->context = &binder_dev->context;
- binder_lock(__func__);
+ binder_lock(proc->context, __func__);
binder_stats_created(BINDER_STAT_PROC);
- hlist_add_head(&proc->proc_node, &binder_procs);
+ hlist_add_head(&proc->proc_node, &proc->context->binder_procs);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
filp->private_data = proc;
- binder_unlock(__func__);
+ binder_unlock(proc->context, __func__);
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
@@ -3628,6 +3641,7 @@ static int binder_release(struct inode *nodp, struct file *filp)
static int binder_node_release(struct binder_node *node, int refs)
{
struct binder_ref *ref;
+ struct binder_context *context = node->proc->context;
int death = 0;
list_del_init(&node->work.entry);
@@ -3643,7 +3657,7 @@ static int binder_node_release(struct binder_node *node, int refs)
node->proc = NULL;
node->local_strong_refs = 0;
node->local_weak_refs = 0;
- hlist_add_head(&node->dead_node, &binder_dead_nodes);
+ hlist_add_head(&node->dead_node, &context->binder_dead_nodes);
hlist_for_each_entry(ref, &node->refs, node_entry) {
refs++;
@@ -3708,7 +3722,8 @@ static void binder_deferred_release(struct binder_proc *proc)
node = rb_entry(n, struct binder_node, rb_node);
nodes++;
rb_erase(&node->rb_node, &proc->nodes);
- incoming_refs = binder_node_release(node, incoming_refs);
+ incoming_refs = binder_node_release(node,
+ incoming_refs);
}
outgoing_refs = 0;
@@ -3780,18 +3795,20 @@ static void binder_deferred_func(struct work_struct *work)
{
struct binder_proc *proc;
struct files_struct *files;
+ struct binder_context *context =
+ container_of(work, struct binder_context, deferred_work);
int defer;
do {
trace_binder_lock(__func__);
- mutex_lock(&binder_main_lock);
+ mutex_lock(&context->binder_main_lock);
trace_binder_locked(__func__);
- mutex_lock(&binder_deferred_lock);
+ mutex_lock(&context->binder_deferred_lock);
preempt_disable();
- if (!hlist_empty(&binder_deferred_list)) {
- proc = hlist_entry(binder_deferred_list.first,
+ if (!hlist_empty(&context->binder_deferred_list)) {
+ proc = hlist_entry(context->binder_deferred_list.first,
struct binder_proc, deferred_work_node);
hlist_del_init(&proc->deferred_work_node);
defer = proc->deferred_work;
@@ -3800,7 +3817,7 @@ static void binder_deferred_func(struct work_struct *work)
proc = NULL;
defer = 0;
}
- mutex_unlock(&binder_deferred_lock);
+ mutex_unlock(&context->binder_deferred_lock);
files = NULL;
if (defer & BINDER_DEFERRED_PUT_FILES) {
@@ -3816,25 +3833,25 @@ static void binder_deferred_func(struct work_struct *work)
binder_deferred_release(proc); /* frees proc */
trace_binder_unlock(__func__);
- mutex_unlock(&binder_main_lock);
+ mutex_unlock(&context->binder_main_lock);
preempt_enable_no_resched();
if (files)
put_files_struct(files);
} while (proc);
}
-static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
{
- mutex_lock(&binder_deferred_lock);
+ mutex_lock(&proc->context->binder_deferred_lock);
proc->deferred_work |= defer;
if (hlist_unhashed(&proc->deferred_work_node)) {
hlist_add_head(&proc->deferred_work_node,
- &binder_deferred_list);
- queue_work(binder_deferred_workqueue, &binder_deferred_work);
+ &proc->context->binder_deferred_list);
+ queue_work(proc->context->binder_deferred_workqueue,
+ &proc->context->deferred_work);
}
- mutex_unlock(&binder_deferred_lock);
+ mutex_unlock(&proc->context->binder_deferred_lock);
}
static void print_binder_transaction(struct seq_file *m, const char *prefix,
@@ -4065,8 +4082,20 @@ static const char * const binder_objstat_strings[] = {
"transaction_complete"
};
+static void add_binder_stats(struct binder_stats *from, struct binder_stats *to)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(to->bc); i++)
+ to->bc[i] += from->bc[i];
+
+ for (i = 0; i < ARRAY_SIZE(to->br); i++)
+ to->br[i] += from->br[i];
+}
+
static void print_binder_stats(struct seq_file *m, const char *prefix,
- struct binder_stats *stats)
+ struct binder_stats *stats,
+ struct binder_obj_stats *obj_stats)
{
int i;
@@ -4086,16 +4115,21 @@ static void print_binder_stats(struct seq_file *m, const char *prefix,
binder_return_strings[i], stats->br[i]);
}
- BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) !=
+ if (!obj_stats)
+ return;
+
+ BUILD_BUG_ON(ARRAY_SIZE(obj_stats->obj_created) !=
ARRAY_SIZE(binder_objstat_strings));
- BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) !=
- ARRAY_SIZE(stats->obj_deleted));
- for (i = 0; i < ARRAY_SIZE(stats->obj_created); i++) {
- if (stats->obj_created[i] || stats->obj_deleted[i])
+ BUILD_BUG_ON(ARRAY_SIZE(obj_stats->obj_created) !=
+ ARRAY_SIZE(obj_stats->obj_deleted));
+ for (i = 0; i < ARRAY_SIZE(obj_stats->obj_created); i++) {
+ int obj_created = atomic_read(&obj_stats->obj_created[i]);
+ int obj_deleted = atomic_read(&obj_stats->obj_deleted[i]);
+
+ if (obj_created || obj_deleted)
seq_printf(m, "%s%s: active %d total %d\n", prefix,
- binder_objstat_strings[i],
- stats->obj_created[i] - stats->obj_deleted[i],
- stats->obj_created[i]);
+ binder_objstat_strings[i],
+ obj_created - obj_deleted, obj_created);
}
}
@@ -4150,85 +4184,131 @@ static void print_binder_proc_stats(struct seq_file *m,
}
seq_printf(m, " pending transactions: %d\n", count);
- print_binder_stats(m, " ", &proc->stats);
+ print_binder_stats(m, " ", &proc->stats, NULL);
}
static int binder_state_show(struct seq_file *m, void *unused)
{
+ struct binder_device *device;
+ struct binder_context *context;
struct binder_proc *proc;
struct binder_node *node;
int do_lock = !binder_debug_no_lock;
-
- if (do_lock)
- binder_lock(__func__);
+ bool wrote_dead_nodes_header = false;
seq_puts(m, "binder state:\n");
- if (!hlist_empty(&binder_dead_nodes))
- seq_puts(m, "dead nodes:\n");
- hlist_for_each_entry(node, &binder_dead_nodes, dead_node)
- print_binder_node(m, node);
+ hlist_for_each_entry(device, &binder_devices, hlist) {
+ context = &device->context;
+ if (do_lock)
+ binder_lock(context, __func__);
+ if (!wrote_dead_nodes_header &&
+ !hlist_empty(&context->binder_dead_nodes)) {
+ seq_puts(m, "dead nodes:\n");
+ wrote_dead_nodes_header = true;
+ }
+ hlist_for_each_entry(node, &context->binder_dead_nodes,
+ dead_node)
+ print_binder_node(m, node);
+
+ if (do_lock)
+ binder_unlock(context, __func__);
+ }
- hlist_for_each_entry(proc, &binder_procs, proc_node)
- print_binder_proc(m, proc, 1);
- if (do_lock)
- binder_unlock(__func__);
+ hlist_for_each_entry(device, &binder_devices, hlist) {
+ context = &device->context;
+ if (do_lock)
+ binder_lock(context, __func__);
+
+ hlist_for_each_entry(proc, &context->binder_procs, proc_node)
+ print_binder_proc(m, proc, 1);
+ if (do_lock)
+ binder_unlock(context, __func__);
+ }
return 0;
}
static int binder_stats_show(struct seq_file *m, void *unused)
{
+ struct binder_device *device;
+ struct binder_context *context;
struct binder_proc *proc;
+ struct binder_stats total_binder_stats;
int do_lock = !binder_debug_no_lock;
- if (do_lock)
- binder_lock(__func__);
+ memset(&total_binder_stats, 0, sizeof(struct binder_stats));
+
+ hlist_for_each_entry(device, &binder_devices, hlist) {
+ context = &device->context;
+ if (do_lock)
+ binder_lock(context, __func__);
+
+ add_binder_stats(&context->binder_stats, &total_binder_stats);
+
+ if (do_lock)
+ binder_unlock(context, __func__);
+ }
seq_puts(m, "binder stats:\n");
+ print_binder_stats(m, "", &total_binder_stats, &binder_obj_stats);
- print_binder_stats(m, "", &binder_stats);
+ hlist_for_each_entry(device, &binder_devices, hlist) {
+ context = &device->context;
+ if (do_lock)
+ binder_lock(context, __func__);
- hlist_for_each_entry(proc, &binder_procs, proc_node)
- print_binder_proc_stats(m, proc);
- if (do_lock)
- binder_unlock(__func__);
+ hlist_for_each_entry(proc, &context->binder_procs, proc_node)
+ print_binder_proc_stats(m, proc);
+ if (do_lock)
+ binder_unlock(context, __func__);
+ }
return 0;
}
static int binder_transactions_show(struct seq_file *m, void *unused)
{
+ struct binder_device *device;
+ struct binder_context *context;
struct binder_proc *proc;
int do_lock = !binder_debug_no_lock;
- if (do_lock)
- binder_lock(__func__);
-
seq_puts(m, "binder transactions:\n");
- hlist_for_each_entry(proc, &binder_procs, proc_node)
- print_binder_proc(m, proc, 0);
- if (do_lock)
- binder_unlock(__func__);
+ hlist_for_each_entry(device, &binder_devices, hlist) {
+ context = &device->context;
+ if (do_lock)
+ binder_lock(context, __func__);
+
+ hlist_for_each_entry(proc, &context->binder_procs, proc_node)
+ print_binder_proc(m, proc, 0);
+ if (do_lock)
+ binder_unlock(context, __func__);
+ }
return 0;
}
static int binder_proc_show(struct seq_file *m, void *unused)
{
+ struct binder_device *device;
+ struct binder_context *context;
struct binder_proc *itr;
int pid = (unsigned long)m->private;
int do_lock = !binder_debug_no_lock;
- if (do_lock)
- binder_lock(__func__);
+ hlist_for_each_entry(device, &binder_devices, hlist) {
+ context = &device->context;
+ if (do_lock)
+ binder_lock(context, __func__);
- hlist_for_each_entry(itr, &binder_procs, proc_node) {
- if (itr->pid == pid) {
- seq_puts(m, "binder proc state:\n");
- print_binder_proc(m, itr, 1);
+ hlist_for_each_entry(itr, &context->binder_procs, proc_node) {
+ if (itr->pid == pid) {
+ seq_puts(m, "binder proc state:\n");
+ print_binder_proc(m, itr, 1);
+ }
}
+ if (do_lock)
+ binder_unlock(context, __func__);
}
- if (do_lock)
- binder_unlock(__func__);
return 0;
}
@@ -4243,11 +4323,10 @@ static void print_binder_transaction_log_entry(struct seq_file *m,
e->to_node, e->target_handle, e->data_size, e->offsets_size);
}
-static int binder_transaction_log_show(struct seq_file *m, void *unused)
+static int print_binder_transaction_log(struct seq_file *m,
+ struct binder_transaction_log *log)
{
- struct binder_transaction_log *log = m->private;
int i;
-
if (log->full) {
for (i = log->next; i < ARRAY_SIZE(log->entry); i++)
print_binder_transaction_log_entry(m, &log->entry[i]);
@@ -4257,6 +4336,31 @@ static int binder_transaction_log_show(struct seq_file *m, void *unused)
return 0;
}
+static int binder_transaction_log_show(struct seq_file *m, void *unused)
+{
+ struct binder_device *device;
+ struct binder_context *context;
+
+ hlist_for_each_entry(device, &binder_devices, hlist) {
+ context = &device->context;
+ print_binder_transaction_log(m, &context->transaction_log);
+ }
+ return 0;
+}
+
+static int binder_failed_transaction_log_show(struct seq_file *m, void *unused)
+{
+ struct binder_device *device;
+ struct binder_context *context;
+
+ hlist_for_each_entry(device, &binder_devices, hlist) {
+ context = &device->context;
+ print_binder_transaction_log(m,
+ &context->transaction_log_failed);
+ }
+ return 0;
+}
+
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
@@ -4272,11 +4376,20 @@ BINDER_DEBUG_ENTRY(state);
BINDER_DEBUG_ENTRY(stats);
BINDER_DEBUG_ENTRY(transactions);
BINDER_DEBUG_ENTRY(transaction_log);
+BINDER_DEBUG_ENTRY(failed_transaction_log);
+
+static void __init free_binder_device(struct binder_device *device)
+{
+ if (device->context.binder_deferred_workqueue)
+ destroy_workqueue(device->context.binder_deferred_workqueue);
+ kfree(device);
+}
static int __init init_binder_device(const char *name)
{
int ret;
struct binder_device *binder_device;
+ struct binder_context *context;
binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
if (!binder_device)
@@ -4286,31 +4399,65 @@ static int __init init_binder_device(const char *name)
binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
binder_device->miscdev.name = name;
- binder_device->context.binder_context_mgr_uid = INVALID_UID;
- binder_device->context.name = name;
+ context = &binder_device->context;
+ context->binder_context_mgr_uid = INVALID_UID;
+ context->name = name;
+
+ mutex_init(&context->binder_main_lock);
+ mutex_init(&context->binder_deferred_lock);
+ mutex_init(&context->binder_mmap_lock);
+
+ context->binder_deferred_workqueue =
+ create_singlethread_workqueue(name);
+
+ if (!context->binder_deferred_workqueue) {
+ ret = -ENOMEM;
+ goto err_create_singlethread_workqueue_failed;
+ }
+
+ INIT_HLIST_HEAD(&context->binder_procs);
+ INIT_HLIST_HEAD(&context->binder_dead_nodes);
+ INIT_HLIST_HEAD(&context->binder_deferred_list);
+ INIT_WORK(&context->deferred_work, binder_deferred_func);
ret = misc_register(&binder_device->miscdev);
if (ret < 0) {
- kfree(binder_device);
- return ret;
+ goto err_misc_register_failed;
}
hlist_add_head(&binder_device->hlist, &binder_devices);
+ return ret;
+
+err_create_singlethread_workqueue_failed:
+err_misc_register_failed:
+ free_binder_device(binder_device);
return ret;
}
static int __init binder_init(void)
{
- int ret;
+ int ret = 0;
char *device_name, *device_names;
struct binder_device *device;
struct hlist_node *tmp;
- binder_deferred_workqueue = create_singlethread_workqueue("binder");
- if (!binder_deferred_workqueue)
+ /*
+ * Copy the module_parameter string, because we don't want to
+ * tokenize it in-place.
+ */
+ device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
+ if (!device_names)
return -ENOMEM;
+ strcpy(device_names, binder_devices_param);
+
+ while ((device_name = strsep(&device_names, ","))) {
+ ret = init_binder_device(device_name);
+ if (ret)
+ goto err_init_binder_device_failed;
+ }
+
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
@@ -4335,30 +4482,13 @@ static int __init binder_init(void)
debugfs_create_file("transaction_log",
S_IRUGO,
binder_debugfs_dir_entry_root,
- &binder_transaction_log,
+ NULL,
&binder_transaction_log_fops);
debugfs_create_file("failed_transaction_log",
S_IRUGO,
binder_debugfs_dir_entry_root,
- &binder_transaction_log_failed,
- &binder_transaction_log_fops);
- }
-
- /*
- * Copy the module_parameter string, because we don't want to
- * tokenize it in-place.
- */
- device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
- if (!device_names) {
- ret = -ENOMEM;
- goto err_alloc_device_names_failed;
- }
- strcpy(device_names, binder_devices_param);
-
- while ((device_name = strsep(&device_names, ","))) {
- ret = init_binder_device(device_name);
- if (ret)
- goto err_init_binder_device_failed;
+ NULL,
+ &binder_failed_transaction_log_fops);
}
return ret;
@@ -4367,12 +4497,8 @@ err_init_binder_device_failed:
hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
misc_deregister(&device->miscdev);
hlist_del(&device->hlist);
- kfree(device);
+ free_binder_device(device);
}
-err_alloc_device_names_failed:
- debugfs_remove_recursive(binder_debugfs_dir_entry_root);
-
- destroy_workqueue(binder_deferred_workqueue);
return ret;
}
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 09c07f519952..0e494108c20c 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -1042,7 +1042,7 @@ static int print_wakeup_source_stats(struct seq_file *m,
active_time = ktime_set(0, 0);
}
- seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",
+ seq_printf(m, "%-32s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",
ws->name, active_count, ws->event_count,
ws->wakeup_count, ws->expire_count,
ktime_to_ms(active_time), ktime_to_ms(total_time),
@@ -1062,7 +1062,7 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
{
struct wakeup_source *ws;
- seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t"
+ seq_puts(m, "name\t\t\t\t\tactive_count\tevent_count\twakeup_count\t"
"expire_count\tactive_since\ttotal_time\tmax_time\t"
"last_change\tprevent_suspend_time\n");
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c
index d61454ac6e84..c93b29281e35 100644
--- a/drivers/bluetooth/btfm_slim_wcn3990.c
+++ b/drivers/bluetooth/btfm_slim_wcn3990.c
@@ -69,6 +69,14 @@ error:
return ret;
}
+static inline int is_fm_port(uint8_t port_num)
+{
+ if (port_num == CHRK_SB_PGD_PORT_TX1_FM ||
+ port_num == CHRK_SB_PGD_PORT_TX2_FM)
+ return 1;
+ else
+ return 0;
+}
int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
uint8_t rxport, uint8_t enable)
@@ -88,9 +96,7 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
goto enable_disable_txport;
/* Multiple Channel Setting - only for FM Tx */
- if (port_num == CHRK_SB_PGD_PORT_TX1_FM ||
- port_num == CHRK_SB_PGD_PORT_TX2_FM) {
-
+ if (is_fm_port(port_num)) {
reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) |
(0x1 << CHRK_SB_PGD_PORT_TX2_FM);
reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
@@ -116,10 +122,14 @@ enable_disable_txport:
reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num);
enable_disable_rxport:
- if (enable)
- /* Set water mark to 1 and enable the port */
- reg_val = CHRK_SB_PGD_PORT_ENABLE | CHRK_SB_PGD_PORT_WM_LB;
- else
+ if (enable) {
+ if (is_fm_port(port_num))
+ reg_val = CHRK_SB_PGD_PORT_ENABLE |
+ CHRK_SB_PGD_PORT_WM_L3;
+ else
+ reg_val = CHRK_SB_PGD_PORT_ENABLE |
+ CHRK_SB_PGD_PORT_WM_LB;
+ } else
reg_val = CHRK_SB_PGD_PORT_DISABLE;
ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ebbe31fee7ae..4bbe4e5f9a6d 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -592,6 +592,16 @@ config DEVPORT
source "drivers/s390/char/Kconfig"
+config MSM_SMD_PKT
+ bool "Enable device interface for some SMD packet ports"
+ default n
+ depends on MSM_SMD
+ help
+ smd_pkt driver provides the interface for the userspace clients
+ to communicate over smd via device nodes. This enable the
+ usersapce clients to read and write to some smd packets channel
+ for MSM chipset.
+
config TILE_SROM
bool "Character-device access via hypervisor to the Tilera SPI ROM"
depends on TILE
@@ -607,7 +617,7 @@ source "drivers/char/xillybus/Kconfig"
config MSM_ADSPRPC
tristate "QTI ADSP RPC driver"
- depends on MSM_GLINK
+ depends on MSM_SMD
help
Provides a communication mechanism that allows for clients to
make remote method invocations across processor boundary to
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7b0bd5408324..77697b8c42c0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
+obj-$(CONFIG_MSM_SMD_PKT) += msm_smd_pkt.o
obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_MMTIMER) += mmtimer.o
obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 479599473381..10c4d8ce2410 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -25,6 +25,7 @@
#include <linux/hash.h>
#include <linux/msm_ion.h>
#include <soc/qcom/secure_buffer.h>
+#include <soc/qcom/smd.h>
#include <soc/qcom/glink.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
@@ -213,6 +214,7 @@ struct fastrpc_channel_ctx {
struct completion work;
struct notifier_block nb;
struct kref kref;
+ int channel;
int sesscount;
int ssrcount;
void *handle;
@@ -238,6 +240,7 @@ struct fastrpc_apps {
spinlock_t hlock;
struct ion_client *client;
struct device *dev;
+ bool glink;
};
struct fastrpc_mmap {
@@ -298,18 +301,21 @@ static struct fastrpc_channel_ctx gcinfo[NUM_CHANNELS] = {
{
.name = "adsprpc-smd",
.subsys = "adsp",
+ .channel = SMD_APPS_QDSP,
.link.link_info.edge = "lpass",
.link.link_info.transport = "smem",
},
{
.name = "mdsprpc-smd",
.subsys = "modem",
+ .channel = SMD_APPS_MODEM,
.link.link_info.edge = "mpss",
.link.link_info.transport = "smem",
},
{
.name = "sdsprpc-smd",
.subsys = "slpi",
+ .channel = SMD_APPS_DSPS,
.link.link_info.edge = "dsps",
.link.link_info.transport = "smem",
.vmid = VMID_SSC_Q6,
@@ -1382,7 +1388,7 @@ static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx,
struct smq_msg *msg = &ctx->msg;
struct fastrpc_file *fl = ctx->fl;
struct fastrpc_channel_ctx *channel_ctx = &fl->apps->channel[fl->cid];
- int err = 0;
+ int err = 0, len;
VERIFY(err, 0 != channel_ctx->chan);
if (err)
@@ -1397,21 +1403,64 @@ static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx,
msg->invoke.page.addr = ctx->buf ? ctx->buf->phys : 0;
msg->invoke.page.size = buf_page_size(ctx->used);
- if (fl->ssrcount != channel_ctx->ssrcount) {
- err = -ECONNRESET;
- goto bail;
+ if (fl->apps->glink) {
+ if (fl->ssrcount != channel_ctx->ssrcount) {
+ err = -ECONNRESET;
+ goto bail;
+ }
+ VERIFY(err, channel_ctx->link.port_state ==
+ FASTRPC_LINK_CONNECTED);
+ if (err)
+ goto bail;
+ err = glink_tx(channel_ctx->chan,
+ (void *)&fl->apps->channel[fl->cid], msg, sizeof(*msg),
+ GLINK_TX_REQ_INTENT);
+ } else {
+ spin_lock(&fl->apps->hlock);
+ len = smd_write((smd_channel_t *)
+ channel_ctx->chan,
+ msg, sizeof(*msg));
+ spin_unlock(&fl->apps->hlock);
+ VERIFY(err, len == sizeof(*msg));
}
- VERIFY(err, channel_ctx->link.port_state ==
- FASTRPC_LINK_CONNECTED);
- if (err)
- goto bail;
- err = glink_tx(channel_ctx->chan,
- (void *)&fl->apps->channel[fl->cid], msg, sizeof(*msg),
- GLINK_TX_REQ_INTENT);
bail:
return err;
}
+static void fastrpc_smd_read_handler(int cid)
+{
+ struct fastrpc_apps *me = &gfa;
+ struct smq_invoke_rsp rsp = {0};
+ int ret = 0;
+
+ do {
+ ret = smd_read_from_cb(me->channel[cid].chan, &rsp,
+ sizeof(rsp));
+ if (ret != sizeof(rsp))
+ break;
+ rsp.ctx = rsp.ctx & ~1;
+ context_notify_user(uint64_to_ptr(rsp.ctx), rsp.retval);
+ } while (ret == sizeof(rsp));
+}
+
+static void smd_event_handler(void *priv, unsigned event)
+{
+ struct fastrpc_apps *me = &gfa;
+ int cid = (int)(uintptr_t)priv;
+
+ switch (event) {
+ case SMD_EVENT_OPEN:
+ complete(&me->channel[cid].work);
+ break;
+ case SMD_EVENT_CLOSE:
+ fastrpc_notify_drivers(me, cid);
+ break;
+ case SMD_EVENT_DATA:
+ fastrpc_smd_read_handler(cid);
+ break;
+ }
+}
+
static void fastrpc_init(struct fastrpc_apps *me)
{
int i;
@@ -1987,10 +2036,12 @@ static void fastrpc_channel_close(struct kref *kref)
ctx = container_of(kref, struct fastrpc_channel_ctx, kref);
cid = ctx - &gcinfo[0];
- fastrpc_glink_close(ctx->chan, cid);
+ if (!me->glink)
+ smd_close(ctx->chan);
+ else
+ fastrpc_glink_close(ctx->chan, cid);
+
ctx->chan = 0;
- glink_unregister_link_state_cb(ctx->link.link_notify_handle);
- ctx->link.link_notify_handle = 0;
mutex_unlock(&me->smd_mutex);
pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name,
MAJOR(me->dev_no), cid);
@@ -2405,8 +2456,16 @@ static int fastrpc_channel_open(struct fastrpc_file *fl)
fl->ssrcount = me->channel[cid].ssrcount;
if ((kref_get_unless_zero(&me->channel[cid].kref) == 0) ||
(me->channel[cid].chan == 0)) {
- fastrpc_glink_register(cid, me);
- VERIFY(err, 0 == fastrpc_glink_open(cid));
+ if (me->glink) {
+ fastrpc_glink_register(cid, me);
+ VERIFY(err, 0 == fastrpc_glink_open(cid));
+ } else {
+ VERIFY(err, !smd_named_open_on_edge(FASTRPC_SMD_GUID,
+ gcinfo[cid].channel,
+ (smd_channel_t **)&me->channel[cid].chan,
+ (void *)(uintptr_t)cid,
+ smd_event_handler));
+ }
if (err)
goto bail;
@@ -2635,7 +2694,11 @@ static int fastrpc_restart_notifier_cb(struct notifier_block *nb,
ctx->ssrcount++;
ctx->issubsystemup = 0;
if (ctx->chan) {
- fastrpc_glink_close(ctx->chan, cid);
+ if (me->glink)
+ fastrpc_glink_close(ctx->chan, cid);
+ else
+ smd_close(ctx->chan);
+
ctx->chan = 0;
pr_info("'restart notifier: closed /dev/%s c %d %d'\n",
gcinfo[cid].name, MAJOR(me->dev_no), cid);
@@ -2859,7 +2922,7 @@ static int fastrpc_probe(struct platform_device *pdev)
}
return 0;
}
-
+ me->glink = of_property_read_bool(dev->of_node, "qcom,fastrpc-glink");
VERIFY(err, !of_platform_populate(pdev->dev.of_node,
fastrpc_match_table,
NULL, &pdev->dev));
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index 437077c4d44d..3c10462c2274 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -457,7 +457,9 @@ static void diag_send_feature_mask_update(uint8_t peripheral)
if (driver->supports_apps_hdlc_encoding)
DIAG_SET_FEATURE_MASK(F_DIAG_APPS_HDLC_ENCODE);
if (driver->supports_apps_header_untagging) {
- if (peripheral == PERIPHERAL_MODEM) {
+ if (peripheral == PERIPHERAL_MODEM ||
+ peripheral == PERIPHERAL_LPASS ||
+ peripheral == PERIPHERAL_CDSP) {
DIAG_SET_FEATURE_MASK(F_DIAG_PKT_HEADER_UNTAG);
driver->peripheral_untag[peripheral] =
ENABLE_PKT_HEADER_UNTAGGING;
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index dc3029cc459d..bd34e6cceec0 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -129,6 +129,37 @@ void diag_md_close_all()
diag_ws_reset(DIAG_WS_MUX);
}
+static int diag_md_get_peripheral(int ctxt)
+{
+ int peripheral;
+
+ if (driver->num_pd_session) {
+ peripheral = GET_PD_CTXT(ctxt);
+ switch (peripheral) {
+ case UPD_WLAN:
+ case UPD_AUDIO:
+ case UPD_SENSORS:
+ break;
+ case DIAG_ID_MPSS:
+ case DIAG_ID_LPASS:
+ case DIAG_ID_CDSP:
+ default:
+ peripheral =
+ GET_BUF_PERIPHERAL(ctxt);
+ if (peripheral > NUM_PERIPHERALS)
+ peripheral = -EINVAL;
+ break;
+ }
+ } else {
+ /* Account for Apps data as well */
+ peripheral = GET_BUF_PERIPHERAL(ctxt);
+ if (peripheral > NUM_PERIPHERALS)
+ peripheral = -EINVAL;
+ }
+
+ return peripheral;
+}
+
int diag_md_write(int id, unsigned char *buf, int len, int ctx)
{
int i;
@@ -144,26 +175,13 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx)
if (!buf || len < 0)
return -EINVAL;
- if (driver->pd_logging_mode) {
- peripheral = GET_PD_CTXT(ctx);
- switch (peripheral) {
- case UPD_WLAN:
- break;
- case DIAG_ID_MPSS:
- default:
- peripheral = GET_BUF_PERIPHERAL(ctx);
- if (peripheral > NUM_PERIPHERALS)
- return -EINVAL;
- break;
- }
- } else {
- /* Account for Apps data as well */
- peripheral = GET_BUF_PERIPHERAL(ctx);
- if (peripheral > NUM_PERIPHERALS)
- return -EINVAL;
- }
+ peripheral =
+ diag_md_get_peripheral(ctx);
+ if (peripheral < 0)
+ return -EINVAL;
- session_info = diag_md_session_get_peripheral(peripheral);
+ session_info =
+ diag_md_session_get_peripheral(peripheral);
if (!session_info)
return -EIO;
@@ -243,31 +261,15 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
entry = &ch->tbl[j];
if (entry->len <= 0)
continue;
- if (driver->pd_logging_mode) {
- peripheral = GET_PD_CTXT(entry->ctx);
- switch (peripheral) {
- case UPD_WLAN:
- break;
- case DIAG_ID_MPSS:
- default:
- peripheral =
- GET_BUF_PERIPHERAL(entry->ctx);
- if (peripheral > NUM_PERIPHERALS)
- goto drop_data;
- break;
- }
- } else {
- /* Account for Apps data as well */
- peripheral = GET_BUF_PERIPHERAL(entry->ctx);
- if (peripheral > NUM_PERIPHERALS)
- goto drop_data;
- }
+
+ peripheral = diag_md_get_peripheral(entry->ctx);
+ if (peripheral < 0)
+ goto drop_data;
session_info =
diag_md_session_get_peripheral(peripheral);
if (!session_info) {
- mutex_unlock(&driver->diagfwd_untag_mutex);
- return -EIO;
+ goto drop_data;
}
if (session_info && info &&
@@ -363,9 +365,15 @@ int diag_md_close_peripheral(int id, uint8_t peripheral)
spin_lock_irqsave(&ch->lock, flags);
for (i = 0; i < ch->num_tbl_entries && !found; i++) {
entry = &ch->tbl[i];
- if ((GET_BUF_PERIPHERAL(entry->ctx) != peripheral) ||
- (GET_PD_CTXT(entry->ctx) != peripheral))
- continue;
+
+ if (peripheral > NUM_PERIPHERALS) {
+ if (GET_PD_CTXT(entry->ctx) != peripheral)
+ continue;
+ } else {
+ if (GET_BUF_PERIPHERAL(entry->ctx) !=
+ peripheral)
+ continue;
+ }
found = 1;
if (ch->ops && ch->ops->write_done) {
ch->ops->write_done(entry->buf, entry->len,
diff --git a/drivers/char/diag/diag_mux.c b/drivers/char/diag/diag_mux.c
index 55c5de1ea9fc..39f4b08d9b0a 100644
--- a/drivers/char/diag/diag_mux.c
+++ b/drivers/char/diag/diag_mux.c
@@ -27,7 +27,7 @@
#include "diag_mux.h"
#include "diag_usb.h"
#include "diag_memorydevice.h"
-
+#include "diag_ipc_logging.h"
struct diag_mux_state_t *diag_mux;
static struct diag_logger_t usb_logger;
@@ -146,7 +146,15 @@ int diag_mux_write(int proc, unsigned char *buf, int len, int ctx)
case DIAG_ID_MPSS:
upd = PERIPHERAL_MODEM;
break;
+ case DIAG_ID_LPASS:
+ upd = PERIPHERAL_LPASS;
+ break;
+ case DIAG_ID_CDSP:
+ upd = PERIPHERAL_CDSP;
+ break;
case UPD_WLAN:
+ case UPD_AUDIO:
+ case UPD_SENSORS:
break;
default:
pr_err("diag: invalid pd ctxt= %d\n", upd);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index b68a47219132..b17538a10ea9 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -76,7 +76,9 @@
| DIAG_CON_LPASS | DIAG_CON_WCNSS \
| DIAG_CON_SENSORS | DIAG_CON_WDSP \
| DIAG_CON_CDSP)
-#define DIAG_CON_UPD_ALL (DIAG_CON_UPD_WLAN)
+#define DIAG_CON_UPD_ALL (DIAG_CON_UPD_WLAN \
+ | DIAG_CON_UPD_AUDIO \
+ | DIAG_CON_UPD_SENSORS)
#define DIAG_STM_MODEM 0x01
#define DIAG_STM_LPASS 0x02
@@ -222,6 +224,10 @@
#define DIAG_ID_APPS 1
#define DIAG_ID_MPSS 2
#define DIAG_ID_WLAN 3
+#define DIAG_ID_LPASS 4
+#define DIAG_ID_CDSP 5
+#define DIAG_ID_AUDIO 6
+#define DIAG_ID_SENSORS 7
/* Number of sessions possible in Memory Device Mode. +1 for Apps data */
#define NUM_MD_SESSIONS (NUM_PERIPHERALS \
@@ -598,10 +604,15 @@ struct diagchar_dev {
int in_busy_dcipktdata;
int logging_mode;
int logging_mask;
- int pd_logging_mode;
+ int pd_logging_mode[NUM_UPD];
+ int pd_session_clear[NUM_UPD];
int num_pd_session;
- int cpd_len_1;
- int cpd_len_2;
+ int cpd_len_1[NUM_PERIPHERALS];
+ int cpd_len_2[NUM_PERIPHERALS];
+ int upd_len_1_a[NUM_PERIPHERALS];
+ int upd_len_1_b[NUM_PERIPHERALS];
+ int upd_len_2_a;
+ int upd_len_2_b;
int mask_check;
uint32_t md_session_mask;
uint8_t md_session_mode;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 4f56696f52e9..682c035c5bd4 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -397,6 +397,10 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask)
ret |= DIAG_CON_CDSP;
if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN))
ret |= DIAG_CON_UPD_WLAN;
+ if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_AUDIO))
+ ret |= DIAG_CON_UPD_AUDIO;
+ if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_SENSORS))
+ ret |= DIAG_CON_UPD_SENSORS;
return ret;
}
int diag_mask_param(void)
@@ -426,8 +430,8 @@ void diag_clear_masks(struct diag_md_session_t *info)
static void diag_close_logging_process(const int pid)
{
- int i;
- int session_peripheral_mask;
+ int i, j;
+ int session_mask;
struct diag_md_session_t *session_info = NULL;
struct diag_logging_mode_param_t params;
@@ -443,27 +447,34 @@ static void diag_close_logging_process(const int pid)
mutex_unlock(&driver->diag_maskclear_mutex);
mutex_lock(&driver->diagchar_mutex);
- session_peripheral_mask = session_info->peripheral_mask;
+
+ session_mask = session_info->peripheral_mask;
diag_md_session_close(session_info);
- mutex_unlock(&driver->diagchar_mutex);
+
for (i = 0; i < NUM_MD_SESSIONS; i++)
- if (MD_PERIPHERAL_MASK(i) & session_peripheral_mask)
+ if (MD_PERIPHERAL_MASK(i) & session_mask)
diag_mux_close_peripheral(DIAG_LOCAL_PROC, i);
params.req_mode = USB_MODE;
params.mode_param = 0;
params.peripheral_mask =
- diag_translate_kernel_to_user_mask(session_peripheral_mask);
- if (driver->pd_logging_mode)
- params.pd_mask =
- diag_translate_kernel_to_user_mask(session_peripheral_mask);
-
- if (session_peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN)) {
- driver->pd_logging_mode--;
- driver->num_pd_session--;
+ diag_translate_kernel_to_user_mask(session_mask);
+
+ for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
+ if (session_mask &
+ MD_PERIPHERAL_MASK(i)) {
+ j = i - UPD_WLAN;
+ driver->pd_session_clear[j] = 1;
+ driver->pd_logging_mode[j] = 0;
+ driver->num_pd_session -= 1;
+ params.pd_mask =
+ diag_translate_kernel_to_user_mask(session_mask);
+ } else
+ params.pd_mask = 0;
}
- mutex_lock(&driver->diagchar_mutex);
+
diag_switch_logging(&params);
+
mutex_unlock(&driver->diagchar_mutex);
}
@@ -985,6 +996,11 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len,
hdlc_disabled = driver->hdlc_disabled;
if (hdlc_disabled) {
payload = *(uint16_t *)(buf + 2);
+ if (payload > DIAG_MAX_HDLC_BUF_SIZE) {
+ pr_err("diag: Dropping packet, payload size is %d\n",
+ payload);
+ return -EBADMSG;
+ }
driver->hdlc_encode_buf_len = payload;
/*
* Adding 4 bytes for start (1 byte), version (1 byte) and
@@ -1562,17 +1578,22 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask)
ret |= (1 << PERIPHERAL_CDSP);
if (peripheral_mask & DIAG_CON_UPD_WLAN)
ret |= (1 << UPD_WLAN);
+ if (peripheral_mask & DIAG_CON_UPD_AUDIO)
+ ret |= (1 << UPD_AUDIO);
+ if (peripheral_mask & DIAG_CON_UPD_SENSORS)
+ ret |= (1 << UPD_SENSORS);
return ret;
}
static int diag_switch_logging(struct diag_logging_mode_param_t *param)
{
- int new_mode;
+ int new_mode, i;
int curr_mode;
int err = 0;
uint8_t do_switch = 1;
uint32_t peripheral_mask = 0;
+ uint8_t peripheral, upd;
if (!param)
return -EINVAL;
@@ -1583,10 +1604,28 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
return -EINVAL;
}
- switch (param->pd_mask) {
- case DIAG_CON_UPD_WLAN:
- if (driver->md_session_map[PERIPHERAL_MODEM] &&
- (MD_PERIPHERAL_MASK(PERIPHERAL_MODEM) &
+ if (param->pd_mask) {
+ switch (param->pd_mask) {
+ case DIAG_CON_UPD_WLAN:
+ peripheral = PERIPHERAL_MODEM;
+ upd = UPD_WLAN;
+ break;
+ case DIAG_CON_UPD_AUDIO:
+ peripheral = PERIPHERAL_LPASS;
+ upd = UPD_AUDIO;
+ break;
+ case DIAG_CON_UPD_SENSORS:
+ peripheral = PERIPHERAL_LPASS;
+ upd = UPD_SENSORS;
+ break;
+ default:
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "asking for mode switch with no pd mask set\n");
+ return -EINVAL;
+ }
+
+ if (driver->md_session_map[peripheral] &&
+ (MD_PERIPHERAL_MASK(peripheral) &
diag_mux->mux_mask)) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"diag_fr: User PD is already logging onto active peripheral logging\n");
@@ -1595,15 +1634,16 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
peripheral_mask =
diag_translate_mask(param->pd_mask);
param->peripheral_mask = peripheral_mask;
- driver->pd_logging_mode++;
- driver->num_pd_session++;
- break;
-
- default:
+ i = upd - UPD_WLAN;
+ if (!driver->pd_session_clear[i]) {
+ driver->pd_logging_mode[i] = 1;
+ driver->num_pd_session += 1;
+ driver->pd_session_clear[i] = 0;
+ }
+ } else {
peripheral_mask =
diag_translate_mask(param->peripheral_mask);
param->peripheral_mask = peripheral_mask;
- break;
}
switch (param->req_mode) {
@@ -1945,9 +1985,36 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg)
return 0;
}
-static int diag_ioctl_query_pd_logging(unsigned long ioarg)
+static int diag_ioctl_query_pd_logging(struct diag_logging_mode_param_t *param)
{
int ret = -EINVAL;
+ int peripheral;
+ char *p_str = NULL;
+
+ if (!param)
+ return -EINVAL;
+
+ if (!param->pd_mask) {
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "query with no pd mask set, returning error\n");
+ return -EINVAL;
+ }
+
+ switch (param->pd_mask) {
+ case DIAG_CON_UPD_WLAN:
+ peripheral = PERIPHERAL_MODEM;
+ p_str = "MODEM";
+ break;
+ case DIAG_CON_UPD_AUDIO:
+ case DIAG_CON_UPD_SENSORS:
+ peripheral = PERIPHERAL_LPASS;
+ p_str = "LPASS";
+ break;
+ default:
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "Invalid pd mask, returning EINVAL\n");
+ return -EINVAL;
+ }
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"diag: %s: Untagging support on APPS is %s\n", __func__,
@@ -1955,12 +2022,13 @@ static int diag_ioctl_query_pd_logging(unsigned long ioarg)
"present" : "absent"));
DIAG_LOG(DIAG_DEBUG_USERSPACE,
- "diag: %s: Tagging support on MODEM is %s\n", __func__,
- (driver->feature[PERIPHERAL_MODEM].untag_header ?
+ "diag: %s: Tagging support on %s is %s\n",
+ __func__, p_str,
+ (driver->feature[peripheral].untag_header ?
"present" : "absent"));
if (driver->supports_apps_header_untagging &&
- driver->feature[PERIPHERAL_MODEM].untag_header)
+ driver->feature[peripheral].untag_header)
ret = 0;
return ret;
@@ -2206,7 +2274,10 @@ long diagchar_compat_ioctl(struct file *filp,
result = diag_ioctl_hdlc_toggle(ioarg);
break;
case DIAG_IOCTL_QUERY_PD_LOGGING:
- result = diag_ioctl_query_pd_logging(ioarg);
+ if (copy_from_user((void *)&mode_param, (void __user *)ioarg,
+ sizeof(mode_param)))
+ return -EFAULT;
+ result = diag_ioctl_query_pd_logging(&mode_param);
break;
}
return result;
@@ -2332,7 +2403,10 @@ long diagchar_ioctl(struct file *filp,
result = diag_ioctl_hdlc_toggle(ioarg);
break;
case DIAG_IOCTL_QUERY_PD_LOGGING:
- result = diag_ioctl_query_pd_logging(ioarg);
+ if (copy_from_user((void *)&mode_param, (void __user *)ioarg,
+ sizeof(mode_param)))
+ return -EFAULT;
+ result = diag_ioctl_query_pd_logging(&mode_param);
break;
}
return result;
@@ -3474,7 +3548,10 @@ static int __init diagchar_init(void)
poolsize_usb_apps + 1 + (NUM_PERIPHERALS * 6));
driver->num_clients = max_clients;
driver->logging_mode = DIAG_USB_MODE;
- driver->pd_logging_mode = 0;
+ for (i = 0; i < NUM_UPD; i++) {
+ driver->pd_logging_mode[i] = 0;
+ driver->pd_session_clear[i] = 0;
+ }
driver->num_pd_session = 0;
driver->mask_check = 0;
driver->in_busy_pktdata = 0;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 07c90b741fa0..8fb724305c03 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -38,6 +38,7 @@
#include "diag_masks.h"
#include "diag_usb.h"
#include "diag_mux.h"
+#include "diag_ipc_logging.h"
#define STM_CMD_VERSION_OFFSET 4
#define STM_CMD_MASK_OFFSET 5
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index ae749725f6db..82a67f1f6f47 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -110,6 +110,8 @@ void diag_notify_md_client(uint8_t peripheral, int data)
{
int stat = 0;
struct siginfo info;
+ struct pid *pid_struct;
+ struct task_struct *result;
if (peripheral > NUM_PERIPHERALS)
return;
@@ -122,20 +124,38 @@ void diag_notify_md_client(uint8_t peripheral, int data)
info.si_code = SI_QUEUE;
info.si_int = (PERIPHERAL_MASK(peripheral) | data);
info.si_signo = SIGCONT;
- if (driver->md_session_map[peripheral] &&
- driver->md_session_map[peripheral]->task) {
- if (driver->md_session_map[peripheral]->
- md_client_thread_info->task != NULL
- && driver->md_session_map[peripheral]->pid ==
- driver->md_session_map[peripheral]->task->tgid) {
+
+ if (!driver->md_session_map[peripheral] ||
+ driver->md_session_map[peripheral]->pid <= 0) {
+ pr_err("diag: md_session_map[%d] is invalid\n", peripheral);
+ mutex_unlock(&driver->md_session_lock);
+ return;
+ }
+
+ pid_struct = find_get_pid(
+ driver->md_session_map[peripheral]->pid);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "md_session_map[%d] pid = %d task = %pK\n",
+ peripheral,
+ driver->md_session_map[peripheral]->pid,
+ driver->md_session_map[peripheral]->task);
+
+ if (pid_struct) {
+ result = get_pid_task(pid_struct, PIDTYPE_PID);
+
+ if (!result) {
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
- "md_session %d pid = %d, md_session %d task tgid = %d\n",
- peripheral,
- driver->md_session_map[peripheral]->pid,
+ "diag: md_session_map[%d] with pid = %d Exited..\n",
peripheral,
- driver->md_session_map[peripheral]->task->tgid);
- stat = send_sig_info(info.si_signo, &info,
- driver->md_session_map[peripheral]->task);
+ driver->md_session_map[peripheral]->pid);
+ mutex_unlock(&driver->md_session_lock);
+ return;
+ }
+
+ if (driver->md_session_map[peripheral] &&
+ driver->md_session_map[peripheral]->task == result) {
+ stat = send_sig_info(info.si_signo,
+ &info, result);
if (stat)
pr_err("diag: Err sending signal to memory device client, signal data: 0x%x, stat: %d\n",
info.si_int, stat);
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 154f181d82f9..e86dc8292bf0 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -244,9 +244,14 @@ static void diagfwd_data_process_done(struct diagfwd_info *fwd_info,
mutex_lock(&driver->hdlc_disable_mutex);
mutex_lock(&fwd_info->data_mutex);
+
peripheral = GET_PD_CTXT(buf->ctxt);
if (peripheral == DIAG_ID_MPSS)
peripheral = PERIPHERAL_MODEM;
+ if (peripheral == DIAG_ID_LPASS)
+ peripheral = PERIPHERAL_LPASS;
+ if (peripheral == DIAG_ID_CDSP)
+ peripheral = PERIPHERAL_CDSP;
session_info =
diag_md_session_get_peripheral(peripheral);
@@ -323,15 +328,19 @@ end:
static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
unsigned char *buf, int len)
{
- int len_cpd = 0, len_upd_1 = 0;
- int ctxt_cpd = 0, ctxt_upd_1 = 0;
+ int len_cpd = 0;
+ int len_upd_1 = 0, len_upd_2 = 0;
+ int ctxt_cpd = 0;
+ int ctxt_upd_1 = 0, ctxt_upd_2 = 0;
int buf_len = 0, processed = 0;
unsigned char *temp_buf_main = NULL;
unsigned char *temp_buf_cpd = NULL;
unsigned char *temp_buf_upd_1 = NULL;
+ unsigned char *temp_buf_upd_2 = NULL;
struct diagfwd_buf_t *temp_ptr_upd = NULL;
struct diagfwd_buf_t *temp_ptr_cpd = NULL;
int flag_buf_1 = 0, flag_buf_2 = 0;
+ uint8_t peripheral;
if (!fwd_info || !buf || len <= 0) {
diag_ws_release();
@@ -349,24 +358,42 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
diag_ws_release();
return;
}
+ peripheral = fwd_info->peripheral;
- if (driver->feature[fwd_info->peripheral].encode_hdlc &&
- driver->feature[fwd_info->peripheral].untag_header &&
- driver->peripheral_untag[fwd_info->peripheral]) {
+ if (driver->feature[peripheral].encode_hdlc &&
+ driver->feature[peripheral].untag_header &&
+ driver->peripheral_untag[peripheral]) {
mutex_lock(&driver->diagfwd_untag_mutex);
temp_buf_cpd = buf;
temp_buf_main = buf;
if (fwd_info->buf_1 &&
fwd_info->buf_1->data_raw == buf) {
flag_buf_1 = 1;
- if (fwd_info->type == TYPE_DATA)
+ temp_ptr_cpd = fwd_info->buf_1;
+ if (fwd_info->type == TYPE_DATA) {
temp_buf_upd_1 =
fwd_info->buf_upd_1_a->data_raw;
- } else {
+ if (peripheral ==
+ PERIPHERAL_LPASS)
+ temp_buf_upd_2 =
+ fwd_info->buf_upd_2_a->data_raw;
+ }
+ } else if (fwd_info->buf_2 &&
+ fwd_info->buf_2->data_raw == buf) {
flag_buf_2 = 1;
+ temp_ptr_cpd = fwd_info->buf_2;
if (fwd_info->type == TYPE_DATA)
temp_buf_upd_1 =
fwd_info->buf_upd_1_b->data_raw;
+ if (peripheral ==
+ PERIPHERAL_LPASS)
+ temp_buf_upd_2 =
+ fwd_info->buf_upd_2_b->data_raw;
+ } else {
+ pr_err("diag: In %s, no match for buffer %pK, peripheral %d, type: %d\n",
+ __func__, buf, peripheral,
+ fwd_info->type);
+ goto end;
}
while (processed < len) {
buf_len =
@@ -390,31 +417,97 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_buf_upd_1 += buf_len;
}
break;
+ case DIAG_ID_LPASS:
+ ctxt_cpd = DIAG_ID_LPASS;
+ len_cpd += buf_len;
+ if (temp_buf_cpd) {
+ memcpy(temp_buf_cpd,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_cpd += buf_len;
+ }
+ break;
+ case DIAG_ID_AUDIO:
+ ctxt_upd_1 = UPD_AUDIO;
+ len_upd_1 += buf_len;
+ if (temp_buf_upd_1) {
+ memcpy(temp_buf_upd_1,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_upd_1 += buf_len;
+ }
+ break;
+ case DIAG_ID_SENSORS:
+ ctxt_upd_2 = UPD_SENSORS;
+ len_upd_2 += buf_len;
+ if (temp_buf_upd_2) {
+ memcpy(temp_buf_upd_2,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_upd_2 += buf_len;
+ }
+ break;
+ case DIAG_ID_CDSP:
+ ctxt_cpd = DIAG_ID_CDSP;
+ len_cpd += buf_len;
+ if (temp_buf_cpd) {
+ memcpy(temp_buf_cpd,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_cpd += buf_len;
+ }
+ break;
+ default:
+ goto end;
}
len = len - 4;
temp_buf_main += (buf_len + 4);
processed += buf_len;
}
- if (fwd_info->type == TYPE_DATA && len_upd_1) {
+ if (peripheral == PERIPHERAL_LPASS &&
+ fwd_info->type == TYPE_DATA && len_upd_2) {
+ if (flag_buf_1) {
+ driver->upd_len_2_a = len_upd_2;
+ temp_ptr_upd = fwd_info->buf_upd_2_a;
+ } else {
+ driver->upd_len_2_b = len_upd_2;
+ temp_ptr_upd = fwd_info->buf_upd_2_b;
+ }
+ temp_ptr_upd->ctxt &= 0x00FFFFFF;
+ temp_ptr_upd->ctxt |=
+ (SET_PD_CTXT(ctxt_upd_2));
+ atomic_set(&temp_ptr_upd->in_busy, 1);
+ diagfwd_data_process_done(fwd_info,
+ temp_ptr_upd, len_upd_2);
+ } else {
if (flag_buf_1)
+ driver->upd_len_2_a = 0;
+ if (flag_buf_2)
+ driver->upd_len_2_b = 0;
+ }
+ if (fwd_info->type == TYPE_DATA && len_upd_1) {
+ if (flag_buf_1) {
+ driver->upd_len_1_a[peripheral] =
+ len_upd_1;
temp_ptr_upd = fwd_info->buf_upd_1_a;
- else
+ } else {
+ driver->upd_len_1_b[peripheral] =
+ len_upd_1;
temp_ptr_upd = fwd_info->buf_upd_1_b;
+ }
temp_ptr_upd->ctxt &= 0x00FFFFFF;
temp_ptr_upd->ctxt |=
(SET_PD_CTXT(ctxt_upd_1));
atomic_set(&temp_ptr_upd->in_busy, 1);
diagfwd_data_process_done(fwd_info,
temp_ptr_upd, len_upd_1);
+ } else {
+ if (flag_buf_1)
+ driver->upd_len_1_a[peripheral] = 0;
+ if (flag_buf_2)
+ driver->upd_len_1_b[peripheral] = 0;
}
if (len_cpd) {
- if (flag_buf_1) {
- driver->cpd_len_1 = len_cpd;
- temp_ptr_cpd = fwd_info->buf_1;
- } else {
- driver->cpd_len_2 = len_cpd;
- temp_ptr_cpd = fwd_info->buf_2;
- }
+ if (flag_buf_1)
+ driver->cpd_len_1[peripheral] = len_cpd;
+ else
+ driver->cpd_len_2[peripheral] = len_cpd;
temp_ptr_cpd->ctxt &= 0x00FFFFFF;
temp_ptr_cpd->ctxt |=
(SET_PD_CTXT(ctxt_cpd));
@@ -422,14 +515,24 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_ptr_cpd, len_cpd);
} else {
if (flag_buf_1)
- driver->cpd_len_1 = 0;
+ driver->cpd_len_1[peripheral] = 0;
if (flag_buf_2)
- driver->cpd_len_2 = 0;
+ driver->cpd_len_2[peripheral] = 0;
}
mutex_unlock(&driver->diagfwd_untag_mutex);
+ return;
} else {
diagfwd_data_read_done(fwd_info, buf, len);
+ return;
+ }
+end:
+ diag_ws_release();
+ mutex_unlock(&driver->diagfwd_untag_mutex);
+ if (temp_ptr_cpd) {
+ diagfwd_write_done(fwd_info->peripheral, fwd_info->type,
+ GET_BUF_NUM(temp_ptr_cpd->ctxt));
}
+ diagfwd_queue_read(fwd_info);
}
static void diagfwd_data_read_done(struct diagfwd_info *fwd_info,
@@ -991,6 +1094,21 @@ static void __diag_fwd_open(struct diagfwd_info *fwd_info)
if (!fwd_info->inited)
return;
+ /*
+ * Logging mode here is reflecting previous mode
+ * status and will be updated to new mode later.
+ *
+ * Keeping the buffers busy for Memory Device Mode.
+ */
+
+ if ((driver->logging_mode != DIAG_USB_MODE) ||
+ driver->usb_connected) {
+ if (fwd_info->buf_1)
+ atomic_set(&fwd_info->buf_1->in_busy, 0);
+ if (fwd_info->buf_2)
+ atomic_set(&fwd_info->buf_2->in_busy, 0);
+ }
+
if (fwd_info->p_ops && fwd_info->p_ops->open)
fwd_info->p_ops->open(fwd_info->ctxt);
@@ -1151,18 +1269,78 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
return;
fwd_info = &peripheral_info[type][peripheral];
- if (ctxt == 1 && fwd_info->buf_1)
+
+ if (ctxt == 1 && fwd_info->buf_1) {
+ /* Buffer 1 for core PD is freed */
atomic_set(&fwd_info->buf_1->in_busy, 0);
- else if (ctxt == 2 && fwd_info->buf_2)
+ driver->cpd_len_1[peripheral] = 0;
+ } else if (ctxt == 2 && fwd_info->buf_2) {
+ /* Buffer 2 for core PD is freed */
atomic_set(&fwd_info->buf_2->in_busy, 0);
- else if (ctxt == 3 && fwd_info->buf_upd_1_a) {
+ driver->cpd_len_2[peripheral] = 0;
+ } else if (ctxt == 3 && fwd_info->buf_upd_1_a) {
+ /* Buffer 1 for user pd 1 is freed */
atomic_set(&fwd_info->buf_upd_1_a->in_busy, 0);
- if (driver->cpd_len_1 == 0)
- atomic_set(&fwd_info->buf_1->in_busy, 0);
+
+ if (peripheral == PERIPHERAL_LPASS) {
+ /* if not data in cpd and other user pd
+ * free the core pd buffer for LPASS
+ */
+ if (!driver->cpd_len_1[PERIPHERAL_LPASS] &&
+ !driver->upd_len_2_a)
+ atomic_set(&fwd_info->buf_1->in_busy, 0);
+ } else {
+ /* if not data in cpd
+ * free the core pd buffer for MPSS
+ */
+ if (!driver->cpd_len_1[PERIPHERAL_MODEM])
+ atomic_set(&fwd_info->buf_1->in_busy, 0);
+ }
+ driver->upd_len_1_a[peripheral] = 0;
+
} else if (ctxt == 4 && fwd_info->buf_upd_1_b) {
+ /* Buffer 2 for user pd 1 is freed */
atomic_set(&fwd_info->buf_upd_1_b->in_busy, 0);
- if (driver->cpd_len_2 == 0)
+ if (peripheral == PERIPHERAL_LPASS) {
+ /* if not data in cpd and other user pd
+ * free the core pd buffer for LPASS
+ */
+ if (!driver->cpd_len_2[peripheral] &&
+ !driver->upd_len_2_b)
+ atomic_set(&fwd_info->buf_2->in_busy, 0);
+ } else {
+ /* if not data in cpd
+ * free the core pd buffer for MPSS
+ */
+ if (!driver->cpd_len_2[PERIPHERAL_MODEM])
+ atomic_set(&fwd_info->buf_2->in_busy, 0);
+ }
+ driver->upd_len_1_b[peripheral] = 0;
+
+ } else if (ctxt == 5 && fwd_info->buf_upd_2_a) {
+ /* Buffer 1 for user pd 2 is freed */
+ atomic_set(&fwd_info->buf_upd_2_a->in_busy, 0);
+ /* if not data in cpd and other user pd
+ * free the core pd buffer for LPASS
+ */
+ if (!driver->cpd_len_1[PERIPHERAL_LPASS] &&
+ !driver->upd_len_1_a[PERIPHERAL_LPASS])
+ atomic_set(&fwd_info->buf_1->in_busy, 0);
+
+ driver->upd_len_2_a = 0;
+
+ } else if (ctxt == 6 && fwd_info->buf_upd_2_b) {
+ /* Buffer 2 for user pd 2 is freed */
+ atomic_set(&fwd_info->buf_upd_2_b->in_busy, 0);
+ /* if not data in cpd and other user pd
+ * free the core pd buffer for LPASS
+ */
+ if (!driver->cpd_len_2[PERIPHERAL_LPASS] &&
+ !driver->upd_len_1_b[PERIPHERAL_LPASS])
atomic_set(&fwd_info->buf_2->in_busy, 0);
+
+ driver->upd_len_2_b = 0;
+
} else
pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt);
@@ -1295,8 +1473,8 @@ static void diagfwd_queue_read(struct diagfwd_info *fwd_info)
void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
{
- unsigned char *temp_buf;
- uint8_t peripheral;
+ struct diagfwd_buf_t *temp_fwd_buf;
+ unsigned char *temp_char_buf;
if (!fwd_info)
return;
@@ -1309,20 +1487,19 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
mutex_lock(&fwd_info->buf_mutex);
- peripheral = fwd_info->peripheral;
-
if (!fwd_info->buf_1) {
fwd_info->buf_1 = kzalloc(sizeof(struct diagfwd_buf_t),
GFP_KERNEL);
- if (!fwd_info->buf_1)
+ if (ZERO_OR_NULL_PTR(fwd_info->buf_1))
goto err;
kmemleak_not_leak(fwd_info->buf_1);
}
+
if (!fwd_info->buf_1->data) {
fwd_info->buf_1->data = kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_1->data)
+ if (ZERO_OR_NULL_PTR(fwd_info->buf_1->data))
goto err;
fwd_info->buf_1->len = PERIPHERAL_BUF_SZ;
kmemleak_not_leak(fwd_info->buf_1->data);
@@ -1334,7 +1511,7 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
if (!fwd_info->buf_2) {
fwd_info->buf_2 = kzalloc(sizeof(struct diagfwd_buf_t),
GFP_KERNEL);
- if (!fwd_info->buf_2)
+ if (ZERO_OR_NULL_PTR(fwd_info->buf_2))
goto err;
kmemleak_not_leak(fwd_info->buf_2);
}
@@ -1343,7 +1520,7 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
fwd_info->buf_2->data = kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_2->data)
+ if (ZERO_OR_NULL_PTR(fwd_info->buf_2->data))
goto err;
fwd_info->buf_2->len = PERIPHERAL_BUF_SZ;
kmemleak_not_leak(fwd_info->buf_2->data);
@@ -1352,53 +1529,117 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
fwd_info->type, 2);
}
- if (driver->feature[peripheral].untag_header &&
- driver->peripheral_untag[peripheral]) {
+ if (driver->feature[fwd_info->peripheral].untag_header) {
if (!fwd_info->buf_upd_1_a) {
fwd_info->buf_upd_1_a =
kzalloc(sizeof(struct diagfwd_buf_t),
GFP_KERNEL);
- if (!fwd_info->buf_upd_1_a)
+ if (ZERO_OR_NULL_PTR(fwd_info->buf_upd_1_a))
goto err;
kmemleak_not_leak(fwd_info->buf_upd_1_a);
}
- if (!fwd_info->buf_upd_1_a->data) {
+ if (fwd_info->buf_upd_1_a &&
+ !fwd_info->buf_upd_1_a->data) {
fwd_info->buf_upd_1_a->data =
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_upd_1_a->data)
+ temp_char_buf = fwd_info->buf_upd_1_a->data;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_upd_1_a->len = PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(fwd_info->buf_upd_1_a->data);
+ kmemleak_not_leak(temp_char_buf);
fwd_info->buf_upd_1_a->ctxt = SET_BUF_CTXT(
fwd_info->peripheral,
fwd_info->type, 3);
}
+
if (!fwd_info->buf_upd_1_b) {
- fwd_info->buf_upd_1_b =
+ fwd_info->buf_upd_1_b =
kzalloc(sizeof(struct diagfwd_buf_t),
GFP_KERNEL);
- if (!fwd_info->buf_upd_1_b)
- goto err;
- kmemleak_not_leak(fwd_info->buf_upd_1_b);
+ if (ZERO_OR_NULL_PTR(fwd_info->buf_upd_1_b))
+ goto err;
+ kmemleak_not_leak(fwd_info->buf_upd_1_b);
}
- if (!fwd_info->buf_upd_1_b->data) {
+ if (fwd_info->buf_upd_1_b &&
+ !fwd_info->buf_upd_1_b->data) {
fwd_info->buf_upd_1_b->data =
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_upd_1_b->data)
+ temp_char_buf =
+ fwd_info->buf_upd_1_b->data;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_upd_1_b->len =
PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(fwd_info->buf_upd_1_b->data);
+ kmemleak_not_leak(temp_char_buf);
fwd_info->buf_upd_1_b->ctxt = SET_BUF_CTXT(
fwd_info->peripheral,
fwd_info->type, 4);
}
+ if (fwd_info->peripheral ==
+ PERIPHERAL_LPASS) {
+ if (!fwd_info->buf_upd_2_a) {
+ fwd_info->buf_upd_2_a =
+ kzalloc(sizeof(struct diagfwd_buf_t),
+ GFP_KERNEL);
+ temp_fwd_buf =
+ fwd_info->buf_upd_2_a;
+ if (ZERO_OR_NULL_PTR(temp_fwd_buf))
+ goto err;
+ kmemleak_not_leak(temp_fwd_buf);
+ }
+
+ if (!fwd_info->buf_upd_2_a->data) {
+ fwd_info->buf_upd_2_a->data =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ temp_char_buf =
+ fwd_info->buf_upd_2_a->data;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
+ goto err;
+ fwd_info->buf_upd_2_a->len =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(temp_char_buf);
+ fwd_info->buf_upd_2_a->ctxt =
+ SET_BUF_CTXT(
+ fwd_info->peripheral,
+ fwd_info->type, 5);
+ }
+ if (!fwd_info->buf_upd_2_b) {
+ fwd_info->buf_upd_2_b =
+ kzalloc(sizeof(struct diagfwd_buf_t),
+ GFP_KERNEL);
+ temp_fwd_buf =
+ fwd_info->buf_upd_2_b;
+ if (ZERO_OR_NULL_PTR(temp_fwd_buf))
+ goto err;
+ kmemleak_not_leak(temp_fwd_buf);
+ }
+
+ if (!fwd_info->buf_upd_2_b->data) {
+ fwd_info->buf_upd_2_b->data =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ temp_char_buf =
+ fwd_info->buf_upd_2_b->data;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
+ goto err;
+ fwd_info->buf_upd_2_b->len =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(temp_char_buf);
+ fwd_info->buf_upd_2_b->ctxt =
+ SET_BUF_CTXT(
+ fwd_info->peripheral,
+ fwd_info->type, 6);
+ }
+ }
}
if (driver->supports_apps_hdlc_encoding) {
@@ -1408,52 +1649,87 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_1->data_raw)
+ temp_char_buf =
+ fwd_info->buf_1->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_1->len_raw =
PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(fwd_info->buf_1->data_raw);
+ kmemleak_not_leak(temp_char_buf);
}
+
if (!fwd_info->buf_2->data_raw) {
fwd_info->buf_2->data_raw =
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_2->data_raw)
+ temp_char_buf =
+ fwd_info->buf_2->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_2->len_raw =
PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(fwd_info->buf_2->data_raw);
+ kmemleak_not_leak(temp_char_buf);
}
if (driver->feature[fwd_info->peripheral].
- untag_header &&
- driver->peripheral_untag[peripheral]) {
- if (!fwd_info->buf_upd_1_a->data_raw) {
+ untag_header) {
+ if (fwd_info->buf_upd_1_a &&
+ !fwd_info->buf_upd_1_a->data_raw) {
fwd_info->buf_upd_1_a->data_raw =
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_upd_1_a->data_raw)
+ temp_char_buf =
+ fwd_info->buf_upd_1_a->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_upd_1_a->len_raw =
PERIPHERAL_BUF_SZ;
- temp_buf =
- fwd_info->buf_upd_1_a->data_raw;
- kmemleak_not_leak(temp_buf);
+ kmemleak_not_leak(temp_char_buf);
}
- if (!fwd_info->buf_upd_1_b->data_raw) {
+
+ if (fwd_info->buf_upd_1_b &&
+ !fwd_info->buf_upd_1_b->data_raw) {
fwd_info->buf_upd_1_b->data_raw =
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_upd_1_b->data_raw)
+ temp_char_buf =
+ fwd_info->buf_upd_1_b->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_upd_1_b->len_raw =
PERIPHERAL_BUF_SZ;
- temp_buf =
- fwd_info->buf_upd_1_b->data_raw;
- kmemleak_not_leak(temp_buf);
+ kmemleak_not_leak(temp_char_buf);
+ }
+ if (fwd_info->peripheral == PERIPHERAL_LPASS
+ && !fwd_info->buf_upd_2_a->data_raw) {
+ fwd_info->buf_upd_2_a->data_raw =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ temp_char_buf =
+ fwd_info->buf_upd_2_a->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
+ goto err;
+ fwd_info->buf_upd_2_a->len_raw =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(temp_char_buf);
+ }
+ if (fwd_info->peripheral == PERIPHERAL_LPASS
+ && !fwd_info->buf_upd_2_b->data_raw) {
+ fwd_info->buf_upd_2_b->data_raw =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ temp_char_buf =
+ fwd_info->buf_upd_2_b->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
+ goto err;
+ fwd_info->buf_upd_2_b->len_raw =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(temp_char_buf);
}
}
}
@@ -1466,10 +1742,12 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
fwd_info->buf_1->data_raw = kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_1->data_raw)
+ temp_char_buf =
+ fwd_info->buf_1->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_1->len_raw = PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(fwd_info->buf_1->data_raw);
+ kmemleak_not_leak(temp_char_buf);
}
}
@@ -1506,6 +1784,38 @@ static void diagfwd_buffers_exit(struct diagfwd_info *fwd_info)
kfree(fwd_info->buf_2);
fwd_info->buf_2 = NULL;
}
+ if (fwd_info->buf_upd_1_a) {
+ kfree(fwd_info->buf_upd_1_a->data);
+ fwd_info->buf_upd_1_a->data = NULL;
+ kfree(fwd_info->buf_upd_1_a->data_raw);
+ fwd_info->buf_upd_1_a->data_raw = NULL;
+ kfree(fwd_info->buf_upd_1_a);
+ fwd_info->buf_upd_1_a = NULL;
+ }
+ if (fwd_info->buf_upd_1_b) {
+ kfree(fwd_info->buf_upd_1_b->data);
+ fwd_info->buf_upd_1_b->data = NULL;
+ kfree(fwd_info->buf_upd_1_b->data_raw);
+ fwd_info->buf_upd_1_b->data_raw = NULL;
+ kfree(fwd_info->buf_upd_1_b);
+ fwd_info->buf_upd_1_b = NULL;
+ }
+ if (fwd_info->buf_upd_2_a) {
+ kfree(fwd_info->buf_upd_2_a->data);
+ fwd_info->buf_upd_2_a->data = NULL;
+ kfree(fwd_info->buf_upd_2_a->data_raw);
+ fwd_info->buf_upd_2_a->data_raw = NULL;
+ kfree(fwd_info->buf_upd_2_a);
+ fwd_info->buf_upd_2_a = NULL;
+ }
+ if (fwd_info->buf_upd_2_b) {
+ kfree(fwd_info->buf_upd_2_b->data);
+ fwd_info->buf_upd_2_b->data = NULL;
+ kfree(fwd_info->buf_upd_2_b->data_raw);
+ fwd_info->buf_upd_2_b->data_raw = NULL;
+ kfree(fwd_info->buf_upd_2_b);
+ fwd_info->buf_upd_2_b = NULL;
+ }
mutex_unlock(&fwd_info->buf_mutex);
}
diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h
index f483da81cc96..760f139ff428 100644
--- a/drivers/char/diag/diagfwd_peripheral.h
+++ b/drivers/char/diag/diagfwd_peripheral.h
@@ -80,6 +80,8 @@ struct diagfwd_info {
struct diagfwd_buf_t *buf_2;
struct diagfwd_buf_t *buf_upd_1_a;
struct diagfwd_buf_t *buf_upd_1_b;
+ struct diagfwd_buf_t *buf_upd_2_a;
+ struct diagfwd_buf_t *buf_upd_2_b;
struct diagfwd_buf_t *buf_ptr[NUM_WRITE_BUFFERS];
struct diag_peripheral_ops *p_ops;
struct diag_channel_ops *c_ops;
diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c
new file mode 100644
index 000000000000..a61d273bfb65
--- /dev/null
+++ b/drivers/char/msm_smd_pkt.c
@@ -0,0 +1,1397 @@
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ *
+ */
+/*
+ * SMD Packet Driver -- Provides a binary SMD non-muxed packet port
+ * interface.
+ */
+
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/msm_smd_pkt.h>
+#include <linux/poll.h>
+#include <soc/qcom/smd.h>
+#include <soc/qcom/smsm.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <asm/ioctls.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/ipc_logging.h>
+
+#define MODULE_NAME "msm_smdpkt"
+#define DEVICE_NAME "smdpkt"
+#define WAKEUPSOURCE_TIMEOUT (2000) /* two seconds */
+
+struct smd_pkt_dev {
+ struct list_head dev_list;
+ char dev_name[SMD_MAX_CH_NAME_LEN];
+ char ch_name[SMD_MAX_CH_NAME_LEN];
+ uint32_t edge;
+
+ struct cdev cdev;
+ struct device *devicep;
+ void *pil;
+
+ struct smd_channel *ch;
+ struct mutex ch_lock;
+ struct mutex rx_lock;
+ struct mutex tx_lock;
+ wait_queue_head_t ch_read_wait_queue;
+ wait_queue_head_t ch_write_wait_queue;
+ wait_queue_head_t ch_opened_wait_queue;
+
+ int i;
+ int ref_cnt;
+
+ int blocking_write;
+ int is_open;
+ int poll_mode;
+ unsigned ch_size;
+ uint open_modem_wait;
+
+ int has_reset;
+ int do_reset_notification;
+ struct completion ch_allocated;
+ struct wakeup_source pa_ws; /* Packet Arrival Wakeup Source */
+ struct work_struct packet_arrival_work;
+ spinlock_t pa_spinlock;
+ int ws_locked;
+};
+
+
+struct smd_pkt_driver {
+ struct list_head list;
+ int ref_cnt;
+ char pdriver_name[SMD_MAX_CH_NAME_LEN];
+ struct platform_driver driver;
+};
+
+static DEFINE_MUTEX(smd_pkt_driver_lock_lha1);
+static LIST_HEAD(smd_pkt_driver_list);
+
+struct class *smd_pkt_classp;
+static dev_t smd_pkt_number;
+static struct delayed_work loopback_work;
+static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
+static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
+static uint32_t is_modem_smsm_inited(void);
+
+static DEFINE_MUTEX(smd_pkt_dev_lock_lha1);
+static LIST_HEAD(smd_pkt_dev_list);
+static int num_smd_pkt_ports;
+
+#define SMD_PKT_IPC_LOG_PAGE_CNT 2
+static void *smd_pkt_ilctxt;
+
+static int msm_smd_pkt_debug_mask;
+module_param_named(debug_mask, msm_smd_pkt_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+enum {
+ SMD_PKT_STATUS = 1U << 0,
+ SMD_PKT_READ = 1U << 1,
+ SMD_PKT_WRITE = 1U << 2,
+ SMD_PKT_POLL = 1U << 5,
+};
+
+#define DEBUG
+
+#ifdef DEBUG
+
+#define SMD_PKT_LOG_STRING(x...) \
+do { \
+ if (smd_pkt_ilctxt) \
+ ipc_log_string(smd_pkt_ilctxt, "<SMD_PKT>: "x); \
+} while (0)
+
+#define D_STATUS(x...) \
+do { \
+ if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \
+ pr_info("Status: "x); \
+ SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_READ(x...) \
+do { \
+ if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \
+ pr_info("Read: "x); \
+ SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_WRITE(x...) \
+do { \
+ if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \
+ pr_info("Write: "x); \
+ SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_POLL(x...) \
+do { \
+ if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \
+ pr_info("Poll: "x); \
+ SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define E_SMD_PKT_SSR(x) \
+do { \
+ if (x->do_reset_notification) \
+ pr_err("%s notifying reset for smd_pkt_dev id:%d\n", \
+ __func__, x->i); \
+} while (0)
+#else
+#define D_STATUS(x...) do {} while (0)
+#define D_READ(x...) do {} while (0)
+#define D_WRITE(x...) do {} while (0)
+#define D_POLL(x...) do {} while (0)
+#define E_SMD_PKT_SSR(x) do {} while (0)
+#endif
+
+static ssize_t open_timeout_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned long tmp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->devicep == d) {
+ if (!kstrtoul(buf, 10, &tmp)) {
+ smd_pkt_devp->open_modem_wait = tmp;
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return n;
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to convert: %s to an int\n",
+ __func__, buf);
+ return -EINVAL;
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+ pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+ return -EINVAL;
+}
+
+static ssize_t open_timeout_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->devicep == d) {
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ smd_pkt_devp->open_modem_wait);
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+ return -EINVAL;
+
+}
+
+static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
+
+/**
+ * loopback_edge_store() - Set the edge type for loopback device
+ * @d: Linux device structure
+ * @attr: Device attribute structure
+ * @buf: Input string
+ * @n: Length of the input string
+ *
+ * This function is used to set the loopback device edge runtime
+ * by writing to the loopback_edge node.
+ */
+static ssize_t loopback_edge_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned long tmp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->devicep == d) {
+ if (!kstrtoul(buf, 10, &tmp)) {
+ smd_pkt_devp->edge = tmp;
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return n;
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to convert: %s to an int\n",
+ __func__, buf);
+ return -EINVAL;
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+ return -EINVAL;
+}
+
+/**
+ * loopback_edge_show() - Get the edge type for loopback device
+ * @d: Linux device structure
+ * @attr: Device attribute structure
+ * @buf: Output buffer
+ *
+ * This function is used to get the loopback device edge runtime
+ * by reading the loopback_edge node.
+ */
+static ssize_t loopback_edge_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->devicep == d) {
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ smd_pkt_devp->edge);
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+ return -EINVAL;
+
+}
+
+static DEVICE_ATTR(loopback_edge, 0664, loopback_edge_show,
+ loopback_edge_store);
+
+static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
+{
+ smd_pkt_devp->do_reset_notification = 0;
+
+ return -ENETRESET;
+}
+
+static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
+{
+ smd_pkt_devp->do_reset_notification = 1;
+ smd_pkt_devp->has_reset = 1;
+
+ smd_pkt_devp->is_open = 0;
+
+ wake_up(&smd_pkt_devp->ch_read_wait_queue);
+ wake_up(&smd_pkt_devp->ch_write_wait_queue);
+ wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
+ D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+}
+
+static void loopback_probe_worker(struct work_struct *work)
+{
+
+ /* Wait for the modem SMSM to be inited for the SMD
+ ** Loopback channel to be allocated at the modem. Since
+ ** the wait need to be done atmost once, using msleep
+ ** doesn't degrade the performance.
+ */
+ if (!is_modem_smsm_inited())
+ schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
+ else
+ smsm_change_state(SMSM_APPS_STATE,
+ 0, SMSM_SMD_LOOPBACK);
+
+}
+
+static void packet_arrival_worker(struct work_struct *work)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned long flags;
+
+ smd_pkt_devp = container_of(work, struct smd_pkt_dev,
+ packet_arrival_work);
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+ if (smd_pkt_devp->ch && smd_pkt_devp->ws_locked) {
+ D_READ("%s locking smd_pkt_dev id:%d wakeup source\n",
+ __func__, smd_pkt_devp->i);
+ /*
+ * Keep system awake long enough to allow userspace client
+ * to process the packet.
+ */
+ __pm_wakeup_event(&smd_pkt_devp->pa_ws, WAKEUPSOURCE_TIMEOUT);
+ }
+ spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+}
+
+static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+ struct smd_pkt_dev *smd_pkt_devp;
+ uint32_t val;
+
+ smd_pkt_devp = file->private_data;
+ if (!smd_pkt_devp)
+ return -EINVAL;
+
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ switch (cmd) {
+ case TIOCMGET:
+ D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ ret = smd_tiocmget(smd_pkt_devp->ch);
+ break;
+ case TIOCMSET:
+ ret = get_user(val, (uint32_t *)arg);
+ if (ret) {
+ pr_err("Error getting TIOCMSET value\n");
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+ return ret;
+ }
+ D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d arg[0x%x]\n",
+ __func__, smd_pkt_devp->i, val);
+ ret = smd_tiocmset(smd_pkt_devp->ch, val, ~val);
+ break;
+ case SMD_PKT_IOCTL_BLOCKING_WRITE:
+ ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
+ break;
+ default:
+ pr_err_ratelimited("%s: Unrecognized ioctl command %d\n",
+ __func__, cmd);
+ ret = -ENOIOCTLCMD;
+ }
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+ return ret;
+}
+
+ssize_t smd_pkt_read(struct file *file,
+ char __user *_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int r;
+ int bytes_read;
+ int pkt_size;
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned long flags;
+ void *buf;
+
+ smd_pkt_devp = file->private_data;
+
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on NULL smd_pkt_dev\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!smd_pkt_devp->ch) {
+ pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return -EINVAL;
+ }
+
+ if (smd_pkt_devp->do_reset_notification) {
+ /* notify client that a reset occurred */
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ return notify_reset(smd_pkt_devp);
+ }
+ D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %zu\n",
+ __func__, smd_pkt_devp->i, count);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+wait_for_packet:
+ r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
+ !smd_pkt_devp->ch ||
+ (smd_cur_packet_size(smd_pkt_devp->ch) > 0
+ && smd_read_avail(smd_pkt_devp->ch)) ||
+ smd_pkt_devp->has_reset);
+
+ mutex_lock(&smd_pkt_devp->rx_lock);
+ if (smd_pkt_devp->has_reset) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ kfree(buf);
+ return notify_reset(smd_pkt_devp);
+ }
+
+ if (!smd_pkt_devp->ch) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ kfree(buf);
+ return -EINVAL;
+ }
+
+ if (r < 0) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ /* qualify error message */
+ if (r != -ERESTARTSYS) {
+ /* we get this anytime a signal comes in */
+ pr_err_ratelimited("%s: wait_event_interruptible on smd_pkt_dev id:%d ret %i\n",
+ __func__, smd_pkt_devp->i, r);
+ }
+ kfree(buf);
+ return r;
+ }
+
+ /* Here we have a whole packet waiting for us */
+ pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
+
+ if (!pkt_size) {
+ pr_err_ratelimited("%s: No data on smd_pkt_dev id:%d, False wakeup\n",
+ __func__, smd_pkt_devp->i);
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ goto wait_for_packet;
+ }
+
+ if (pkt_size < 0) {
+ pr_err_ratelimited("%s: Error %d obtaining packet size for Channel %s",
+ __func__, pkt_size, smd_pkt_devp->ch_name);
+ kfree(buf);
+ return pkt_size;
+ }
+
+ if ((uint32_t)pkt_size > count) {
+ pr_err_ratelimited("%s: failure on smd_pkt_dev id: %d - packet size %d > buffer size %zu,",
+ __func__, smd_pkt_devp->i,
+ pkt_size, count);
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ kfree(buf);
+ return -ETOOSMALL;
+ }
+
+ bytes_read = 0;
+ do {
+ r = smd_read(smd_pkt_devp->ch,
+ (buf + bytes_read),
+ (pkt_size - bytes_read));
+ if (r < 0) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ if (smd_pkt_devp->has_reset) {
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ return notify_reset(smd_pkt_devp);
+ }
+ pr_err_ratelimited("%s Error while reading %d\n",
+ __func__, r);
+ kfree(buf);
+ return r;
+ }
+ bytes_read += r;
+ if (pkt_size != bytes_read)
+ wait_event(smd_pkt_devp->ch_read_wait_queue,
+ smd_read_avail(smd_pkt_devp->ch) ||
+ smd_pkt_devp->has_reset);
+ if (smd_pkt_devp->has_reset) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ kfree(buf);
+ return notify_reset(smd_pkt_devp);
+ }
+ } while (pkt_size != bytes_read);
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+ if (smd_pkt_devp->poll_mode &&
+ !smd_cur_packet_size(smd_pkt_devp->ch)) {
+ __pm_relax(&smd_pkt_devp->pa_ws);
+ smd_pkt_devp->ws_locked = 0;
+ smd_pkt_devp->poll_mode = 0;
+ D_READ("%s unlocked smd_pkt_dev id:%d wakeup_source\n",
+ __func__, smd_pkt_devp->i);
+ }
+ spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+ r = copy_to_user(_buf, buf, bytes_read);
+ if (r) {
+ kfree(buf);
+ return -EFAULT;
+ }
+ D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n",
+ __func__, smd_pkt_devp->i, bytes_read);
+ kfree(buf);
+
+ /* check and wakeup read threads waiting on this device */
+ check_and_wakeup_reader(smd_pkt_devp);
+
+ return bytes_read;
+}
+
+ssize_t smd_pkt_write(struct file *file,
+ const char __user *_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int r = 0, bytes_written;
+ struct smd_pkt_dev *smd_pkt_devp;
+ DEFINE_WAIT(write_wait);
+ void *buf;
+
+ smd_pkt_devp = file->private_data;
+
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on NULL smd_pkt_dev\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!smd_pkt_devp->ch) {
+ pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return -EINVAL;
+ }
+
+ if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) {
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ /* notify client that a reset occurred */
+ return notify_reset(smd_pkt_devp);
+ }
+ D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %zu\n",
+ __func__, smd_pkt_devp->i, count);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ r = copy_from_user(buf, _buf, count);
+ if (r) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ mutex_lock(&smd_pkt_devp->tx_lock);
+ if (!smd_pkt_devp->blocking_write) {
+ if (smd_write_avail(smd_pkt_devp->ch) < count) {
+ pr_err_ratelimited("%s: Not enough space in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ kfree(buf);
+ return -ENOMEM;
+ }
+ }
+
+ r = smd_write_start(smd_pkt_devp->ch, count);
+ if (r < 0) {
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ pr_err_ratelimited("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n",
+ __func__, r, smd_pkt_devp->i);
+ kfree(buf);
+ return r;
+ }
+
+ bytes_written = 0;
+ do {
+ prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
+ &write_wait, TASK_UNINTERRUPTIBLE);
+ if (!smd_write_segment_avail(smd_pkt_devp->ch) &&
+ !smd_pkt_devp->has_reset) {
+ smd_enable_read_intr(smd_pkt_devp->ch);
+ schedule();
+ }
+ finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
+ smd_disable_read_intr(smd_pkt_devp->ch);
+
+ if (smd_pkt_devp->has_reset) {
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ kfree(buf);
+ return notify_reset(smd_pkt_devp);
+ }
+ r = smd_write_segment(smd_pkt_devp->ch,
+ (void *)(buf + bytes_written),
+ (count - bytes_written));
+ if (r < 0) {
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ if (smd_pkt_devp->has_reset) {
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ return notify_reset(smd_pkt_devp);
+ }
+ pr_err_ratelimited("%s on smd_pkt_dev id:%d failed r:%d\n",
+ __func__, smd_pkt_devp->i, r);
+ kfree(buf);
+ return r;
+ }
+ bytes_written += r;
+ } while (bytes_written != count);
+ smd_write_end(smd_pkt_devp->ch);
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ D_WRITE("Finished %s on smd_pkt_dev id:%d %zu bytes\n",
+ __func__, smd_pkt_devp->i, count);
+
+ kfree(buf);
+ return count;
+}
+
+static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned int mask = 0;
+
+ smd_pkt_devp = file->private_data;
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on a NULL device\n", __func__);
+ return POLLERR;
+ }
+
+ smd_pkt_devp->poll_mode = 1;
+ poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) {
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+ return POLLERR;
+ }
+
+ if (smd_read_avail(smd_pkt_devp->ch)) {
+ mask |= POLLIN | POLLRDNORM;
+ D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n",
+ __func__, smd_pkt_devp->i);
+ }
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+ return mask;
+}
+
+static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
+{
+ int sz;
+ unsigned long flags;
+
+ if (!smd_pkt_devp) {
+ pr_err("%s on a NULL device\n", __func__);
+ return;
+ }
+
+ if (!smd_pkt_devp->ch) {
+ pr_err("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return;
+ }
+
+ sz = smd_cur_packet_size(smd_pkt_devp->ch);
+ if (sz == 0) {
+ D_READ("%s: No packet in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return;
+ }
+ if (!smd_read_avail(smd_pkt_devp->ch)) {
+ D_READ(
+ "%s: packet size is %d in smd_pkt_dev id:%d - but the data isn't here\n",
+ __func__, sz, smd_pkt_devp->i);
+ return;
+ }
+
+ /* here we have a packet of size sz ready */
+ spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+ __pm_stay_awake(&smd_pkt_devp->pa_ws);
+ smd_pkt_devp->ws_locked = 1;
+ spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+ wake_up(&smd_pkt_devp->ch_read_wait_queue);
+ schedule_work(&smd_pkt_devp->packet_arrival_work);
+ D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+}
+
+static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
+{
+ int sz;
+
+ if (!smd_pkt_devp) {
+ pr_err("%s on a NULL device\n", __func__);
+ return;
+ }
+
+ if (!smd_pkt_devp->ch) {
+ pr_err("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return;
+ }
+
+ sz = smd_write_segment_avail(smd_pkt_devp->ch);
+ if (sz) {
+ D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n",
+ __func__, sz, smd_pkt_devp->i);
+ smd_disable_read_intr(smd_pkt_devp->ch);
+ wake_up(&smd_pkt_devp->ch_write_wait_queue);
+ }
+}
+
+static void ch_notify(void *priv, unsigned event)
+{
+ struct smd_pkt_dev *smd_pkt_devp = priv;
+
+ if (smd_pkt_devp->ch == 0) {
+ if (event != SMD_EVENT_CLOSE)
+ pr_err("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return;
+ }
+
+ switch (event) {
+ case SMD_EVENT_DATA: {
+ D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ check_and_wakeup_reader(smd_pkt_devp);
+ if (smd_pkt_devp->blocking_write)
+ check_and_wakeup_writer(smd_pkt_devp);
+ break;
+ }
+ case SMD_EVENT_OPEN:
+ D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ smd_pkt_devp->has_reset = 0;
+ smd_pkt_devp->is_open = 1;
+ wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
+ break;
+ case SMD_EVENT_CLOSE:
+ D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ smd_pkt_devp->is_open = 0;
+ /* put port into reset state */
+ clean_and_signal(smd_pkt_devp);
+ if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK"))
+ schedule_delayed_work(&loopback_work,
+ msecs_to_jiffies(1000));
+ break;
+ }
+}
+
+static int smd_pkt_dummy_probe(struct platform_device *pdev)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->edge == pdev->id
+ && !strcmp(pdev->name, smd_pkt_devp->ch_name)) {
+ complete_all(&smd_pkt_devp->ch_allocated);
+ D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ break;
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return 0;
+}
+
+static uint32_t is_modem_smsm_inited(void)
+{
+ uint32_t modem_state;
+ uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
+
+ modem_state = smsm_get_state(SMSM_MODEM_STATE);
+ return (modem_state & ready_state) == ready_state;
+}
+
+/**
+ * smd_pkt_add_driver() - Add platform drivers for smd pkt device
+ *
+ * @smd_pkt_devp: pointer to the smd pkt device structure
+ *
+ * @returns: 0 for success, standard Linux error code otherwise
+ *
+ * This function is used to register platform driver once for all
+ * smd pkt devices which have same names and increment the reference
+ * count for 2nd to nth devices.
+ */
+static int smd_pkt_add_driver(struct smd_pkt_dev *smd_pkt_devp)
+{
+ int r = 0;
+ struct smd_pkt_driver *smd_pkt_driverp;
+ struct smd_pkt_driver *item;
+
+ if (!smd_pkt_devp) {
+ pr_err("%s on a NULL device\n", __func__);
+ return -EINVAL;
+ }
+ D_STATUS("Begin %s on smd_pkt_ch[%s]\n", __func__,
+ smd_pkt_devp->ch_name);
+
+ mutex_lock(&smd_pkt_driver_lock_lha1);
+ list_for_each_entry(item, &smd_pkt_driver_list, list) {
+ if (!strcmp(item->pdriver_name, smd_pkt_devp->ch_name)) {
+ D_STATUS("%s:%s Already Platform driver reg. cnt:%d\n",
+ __func__, smd_pkt_devp->ch_name, item->ref_cnt);
+ ++item->ref_cnt;
+ goto exit;
+ }
+ }
+
+ smd_pkt_driverp = kzalloc(sizeof(*smd_pkt_driverp), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(smd_pkt_driverp)) {
+ pr_err("%s: kzalloc() failed for smd_pkt_driver[%s]\n",
+ __func__, smd_pkt_devp->ch_name);
+ r = -ENOMEM;
+ goto exit;
+ }
+
+ smd_pkt_driverp->driver.probe = smd_pkt_dummy_probe;
+ scnprintf(smd_pkt_driverp->pdriver_name, SMD_MAX_CH_NAME_LEN,
+ "%s", smd_pkt_devp->ch_name);
+ smd_pkt_driverp->driver.driver.name = smd_pkt_driverp->pdriver_name;
+ smd_pkt_driverp->driver.driver.owner = THIS_MODULE;
+ r = platform_driver_register(&smd_pkt_driverp->driver);
+ if (r) {
+ pr_err("%s: %s Platform driver reg. failed\n",
+ __func__, smd_pkt_devp->ch_name);
+ kfree(smd_pkt_driverp);
+ goto exit;
+ }
+ ++smd_pkt_driverp->ref_cnt;
+ list_add(&smd_pkt_driverp->list, &smd_pkt_driver_list);
+
+exit:
+ D_STATUS("End %s on smd_pkt_ch[%s]\n", __func__, smd_pkt_devp->ch_name);
+ mutex_unlock(&smd_pkt_driver_lock_lha1);
+ return r;
+}
+
+/**
+ * smd_pkt_remove_driver() - Remove the platform drivers for smd pkt device
+ *
+ * @smd_pkt_devp: pointer to the smd pkt device structure
+ *
+ * This function is used to decrement the reference count on
+ * platform drivers for smd pkt devices and removes the drivers
+ * when the reference count becomes zero.
+ */
+static void smd_pkt_remove_driver(struct smd_pkt_dev *smd_pkt_devp)
+{
+ struct smd_pkt_driver *smd_pkt_driverp;
+ bool found_item = false;
+
+ if (!smd_pkt_devp) {
+ pr_err("%s on a NULL device\n", __func__);
+ return;
+ }
+
+ D_STATUS("Begin %s on smd_pkt_ch[%s]\n", __func__,
+ smd_pkt_devp->ch_name);
+ mutex_lock(&smd_pkt_driver_lock_lha1);
+ list_for_each_entry(smd_pkt_driverp, &smd_pkt_driver_list, list) {
+ if (!strcmp(smd_pkt_driverp->pdriver_name,
+ smd_pkt_devp->ch_name)) {
+ found_item = true;
+ D_STATUS("%s:%s Platform driver cnt:%d\n",
+ __func__, smd_pkt_devp->ch_name,
+ smd_pkt_driverp->ref_cnt);
+ if (smd_pkt_driverp->ref_cnt > 0)
+ --smd_pkt_driverp->ref_cnt;
+ else
+ pr_warn("%s reference count <= 0\n", __func__);
+ break;
+ }
+ }
+ if (!found_item)
+ pr_err("%s:%s No item found in list.\n",
+ __func__, smd_pkt_devp->ch_name);
+
+ if (found_item && smd_pkt_driverp->ref_cnt == 0) {
+ platform_driver_unregister(&smd_pkt_driverp->driver);
+ smd_pkt_driverp->driver.probe = NULL;
+ list_del(&smd_pkt_driverp->list);
+ kfree(smd_pkt_driverp);
+ }
+ mutex_unlock(&smd_pkt_driver_lock_lha1);
+ D_STATUS("End %s on smd_pkt_ch[%s]\n", __func__, smd_pkt_devp->ch_name);
+}
+
+int smd_pkt_open(struct inode *inode, struct file *file)
+{
+ int r = 0;
+ struct smd_pkt_dev *smd_pkt_devp;
+ const char *peripheral = NULL;
+
+ smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
+
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on a NULL device\n", __func__);
+ return -EINVAL;
+ }
+ D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+
+ file->private_data = smd_pkt_devp;
+
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ if (smd_pkt_devp->ch == 0) {
+ unsigned open_wait_rem = smd_pkt_devp->open_modem_wait * 1000;
+
+ reinit_completion(&smd_pkt_devp->ch_allocated);
+
+ r = smd_pkt_add_driver(smd_pkt_devp);
+ if (r) {
+ pr_err_ratelimited("%s: %s Platform driver reg. failed\n",
+ __func__, smd_pkt_devp->ch_name);
+ goto out;
+ }
+
+ peripheral = smd_edge_to_pil_str(smd_pkt_devp->edge);
+ if (!IS_ERR_OR_NULL(peripheral)) {
+ smd_pkt_devp->pil = subsystem_get(peripheral);
+ if (IS_ERR(smd_pkt_devp->pil)) {
+ r = PTR_ERR(smd_pkt_devp->pil);
+ pr_err_ratelimited("%s failed on smd_pkt_dev id:%d - subsystem_get failed for %s\n",
+ __func__, smd_pkt_devp->i, peripheral);
+ /*
+ * Sleep inorder to reduce the frequency of
+ * retry by user-space modules and to avoid
+ * possible watchdog bite.
+ */
+ msleep(open_wait_rem);
+ goto release_pd;
+ }
+ }
+
+ /* Wait for the modem SMSM to be inited for the SMD
+ ** Loopback channel to be allocated at the modem. Since
+ ** the wait need to be done atmost once, using msleep
+ ** doesn't degrade the performance.
+ */
+ if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) {
+ if (!is_modem_smsm_inited())
+ msleep(5000);
+ smsm_change_state(SMSM_APPS_STATE,
+ 0, SMSM_SMD_LOOPBACK);
+ msleep(100);
+ }
+
+ /*
+ * Wait for a packet channel to be allocated so we know
+ * the modem is ready enough.
+ */
+ if (open_wait_rem) {
+ r = wait_for_completion_interruptible_timeout(
+ &smd_pkt_devp->ch_allocated,
+ msecs_to_jiffies(open_wait_rem));
+ if (r >= 0)
+ open_wait_rem = jiffies_to_msecs(r);
+ if (r == 0)
+ r = -ETIMEDOUT;
+ if (r == -ERESTARTSYS) {
+ pr_info_ratelimited("%s: wait on smd_pkt_dev id:%d allocation interrupted\n",
+ __func__, smd_pkt_devp->i);
+ goto release_pil;
+ }
+ if (r < 0) {
+ pr_err_ratelimited("%s: wait on smd_pkt_dev id:%d allocation failed rc:%d\n",
+ __func__, smd_pkt_devp->i, r);
+ goto release_pil;
+ }
+ }
+
+ r = smd_named_open_on_edge(smd_pkt_devp->ch_name,
+ smd_pkt_devp->edge,
+ &smd_pkt_devp->ch,
+ smd_pkt_devp,
+ ch_notify);
+ if (r < 0) {
+ pr_err_ratelimited("%s: %s open failed %d\n", __func__,
+ smd_pkt_devp->ch_name, r);
+ goto release_pil;
+ }
+
+ open_wait_rem = max_t(unsigned, 2000, open_wait_rem);
+ r = wait_event_interruptible_timeout(
+ smd_pkt_devp->ch_opened_wait_queue,
+ smd_pkt_devp->is_open,
+ msecs_to_jiffies(open_wait_rem));
+ if (r == 0)
+ r = -ETIMEDOUT;
+
+ if (r < 0) {
+ /* close the ch to sync smd's state with smd_pkt */
+ smd_close(smd_pkt_devp->ch);
+ smd_pkt_devp->ch = NULL;
+ }
+
+ if (r == -ERESTARTSYS) {
+ pr_info_ratelimited("%s: wait on smd_pkt_dev id:%d OPEN interrupted\n",
+ __func__, smd_pkt_devp->i);
+ } else if (r < 0) {
+ pr_err_ratelimited("%s: wait on smd_pkt_dev id:%d OPEN event failed rc:%d\n",
+ __func__, smd_pkt_devp->i, r);
+ } else if (!smd_pkt_devp->is_open) {
+ pr_err_ratelimited("%s: Invalid OPEN event on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ r = -ENODEV;
+ } else {
+ smd_disable_read_intr(smd_pkt_devp->ch);
+ smd_pkt_devp->ch_size =
+ smd_write_avail(smd_pkt_devp->ch);
+ r = 0;
+ smd_pkt_devp->ref_cnt++;
+ D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ }
+ } else {
+ smd_pkt_devp->ref_cnt++;
+ }
+release_pil:
+ if (peripheral && (r < 0)) {
+ subsystem_put(smd_pkt_devp->pil);
+ smd_pkt_devp->pil = NULL;
+ }
+
+release_pd:
+ if (r < 0)
+ smd_pkt_remove_driver(smd_pkt_devp);
+out:
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+
+ return r;
+}
+
+int smd_pkt_release(struct inode *inode, struct file *file)
+{
+ int r = 0;
+ struct smd_pkt_dev *smd_pkt_devp = file->private_data;
+ unsigned long flags;
+
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on a NULL device\n", __func__);
+ return -EINVAL;
+ }
+ D_STATUS("Begin %s on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ mutex_lock(&smd_pkt_devp->rx_lock);
+ mutex_lock(&smd_pkt_devp->tx_lock);
+ if (smd_pkt_devp->ref_cnt > 0)
+ smd_pkt_devp->ref_cnt--;
+
+ if (smd_pkt_devp->ch != 0 && smd_pkt_devp->ref_cnt == 0) {
+ clean_and_signal(smd_pkt_devp);
+ r = smd_close(smd_pkt_devp->ch);
+ smd_pkt_devp->ch = 0;
+ smd_pkt_devp->blocking_write = 0;
+ smd_pkt_devp->poll_mode = 0;
+ smd_pkt_remove_driver(smd_pkt_devp);
+ if (smd_pkt_devp->pil)
+ subsystem_put(smd_pkt_devp->pil);
+ smd_pkt_devp->has_reset = 0;
+ smd_pkt_devp->do_reset_notification = 0;
+ spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+ if (smd_pkt_devp->ws_locked) {
+ __pm_relax(&smd_pkt_devp->pa_ws);
+ smd_pkt_devp->ws_locked = 0;
+ }
+ spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+ }
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+ if (flush_work(&smd_pkt_devp->packet_arrival_work))
+ D_STATUS("%s: Flushed work for smd_pkt_dev id:%d\n", __func__,
+ smd_pkt_devp->i);
+
+ D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+
+ return r;
+}
+
+static const struct file_operations smd_pkt_fops = {
+ .owner = THIS_MODULE,
+ .open = smd_pkt_open,
+ .release = smd_pkt_release,
+ .read = smd_pkt_read,
+ .write = smd_pkt_write,
+ .poll = smd_pkt_poll,
+ .unlocked_ioctl = smd_pkt_ioctl,
+ .compat_ioctl = smd_pkt_ioctl,
+};
+
+static int smd_pkt_init_add_device(struct smd_pkt_dev *smd_pkt_devp, int i)
+{
+ int r = 0;
+
+ smd_pkt_devp->i = i;
+
+ init_waitqueue_head(&smd_pkt_devp->ch_read_wait_queue);
+ init_waitqueue_head(&smd_pkt_devp->ch_write_wait_queue);
+ smd_pkt_devp->is_open = 0;
+ smd_pkt_devp->poll_mode = 0;
+ smd_pkt_devp->ws_locked = 0;
+ init_waitqueue_head(&smd_pkt_devp->ch_opened_wait_queue);
+
+ spin_lock_init(&smd_pkt_devp->pa_spinlock);
+ mutex_init(&smd_pkt_devp->ch_lock);
+ mutex_init(&smd_pkt_devp->rx_lock);
+ mutex_init(&smd_pkt_devp->tx_lock);
+ wakeup_source_init(&smd_pkt_devp->pa_ws, smd_pkt_devp->dev_name);
+ INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker);
+ init_completion(&smd_pkt_devp->ch_allocated);
+
+ cdev_init(&smd_pkt_devp->cdev, &smd_pkt_fops);
+ smd_pkt_devp->cdev.owner = THIS_MODULE;
+
+ r = cdev_add(&smd_pkt_devp->cdev, (smd_pkt_number + i), 1);
+ if (IS_ERR_VALUE(r)) {
+ pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d ret:%i\n",
+ __func__, i, r);
+ return r;
+ }
+
+ smd_pkt_devp->devicep =
+ device_create(smd_pkt_classp,
+ NULL,
+ (smd_pkt_number + i),
+ NULL,
+ smd_pkt_devp->dev_name);
+
+ if (IS_ERR_OR_NULL(smd_pkt_devp->devicep)) {
+ pr_err("%s: device_create() failed for smd_pkt_dev id:%d\n",
+ __func__, i);
+ r = -ENOMEM;
+ cdev_del(&smd_pkt_devp->cdev);
+ wakeup_source_trash(&smd_pkt_devp->pa_ws);
+ return r;
+ }
+ if (device_create_file(smd_pkt_devp->devicep,
+ &dev_attr_open_timeout))
+ pr_err("%s: unable to create device attr for smd_pkt_dev id:%d\n",
+ __func__, i);
+
+ if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) {
+ if (device_create_file(smd_pkt_devp->devicep,
+ &dev_attr_loopback_edge))
+ pr_err("%s: unable to create device attr for smd_pkt_dev id:%d\n",
+ __func__, i);
+ }
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_add(&smd_pkt_devp->dev_list, &smd_pkt_dev_list);
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+ return r;
+}
+
+static void smd_pkt_core_deinit(void)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ struct smd_pkt_dev *index;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry_safe(smd_pkt_devp, index, &smd_pkt_dev_list,
+ dev_list) {
+ cdev_del(&smd_pkt_devp->cdev);
+ list_del(&smd_pkt_devp->dev_list);
+ device_destroy(smd_pkt_classp,
+ MKDEV(MAJOR(smd_pkt_number), smd_pkt_devp->i));
+ kfree(smd_pkt_devp);
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+ if (!IS_ERR_OR_NULL(smd_pkt_classp))
+ class_destroy(smd_pkt_classp);
+
+ unregister_chrdev_region(MAJOR(smd_pkt_number), num_smd_pkt_ports);
+}
+
+static int smd_pkt_alloc_chrdev_region(void)
+{
+ int r = alloc_chrdev_region(&smd_pkt_number,
+ 0,
+ num_smd_pkt_ports,
+ DEVICE_NAME);
+
+ if (IS_ERR_VALUE(r)) {
+ pr_err("%s: alloc_chrdev_region() failed ret:%i\n",
+ __func__, r);
+ return r;
+ }
+
+ smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(smd_pkt_classp)) {
+ pr_err("%s: class_create() failed ENOMEM\n", __func__);
+ r = -ENOMEM;
+ unregister_chrdev_region(MAJOR(smd_pkt_number),
+ num_smd_pkt_ports);
+ return r;
+ }
+
+ return 0;
+}
+
+static int parse_smdpkt_devicetree(struct device_node *node,
+ struct smd_pkt_dev *smd_pkt_devp)
+{
+ int edge;
+ char *key;
+ const char *ch_name;
+ const char *dev_name;
+ const char *remote_ss;
+
+ key = "qcom,smdpkt-remote";
+ remote_ss = of_get_property(node, key, NULL);
+ if (!remote_ss)
+ goto error;
+
+ edge = smd_remote_ss_to_edge(remote_ss);
+ if (edge < 0)
+ goto error;
+
+ smd_pkt_devp->edge = edge;
+ D_STATUS("%s: %s = %d", __func__, key, edge);
+
+ key = "qcom,smdpkt-port-name";
+ ch_name = of_get_property(node, key, NULL);
+ if (!ch_name)
+ goto error;
+
+ strlcpy(smd_pkt_devp->ch_name, ch_name, SMD_MAX_CH_NAME_LEN);
+ D_STATUS("%s ch_name = %s\n", __func__, ch_name);
+
+ key = "qcom,smdpkt-dev-name";
+ dev_name = of_get_property(node, key, NULL);
+ if (!dev_name)
+ goto error;
+
+ strlcpy(smd_pkt_devp->dev_name, dev_name, SMD_MAX_CH_NAME_LEN);
+ D_STATUS("%s dev_name = %s\n", __func__, dev_name);
+
+ return 0;
+
+error:
+ pr_err("%s: missing key: %s\n", __func__, key);
+ return -ENODEV;
+
+}
+
+static int smd_pkt_devicetree_init(struct platform_device *pdev)
+{
+ int ret;
+ int i = 0;
+ struct device_node *node;
+ struct smd_pkt_dev *smd_pkt_devp;
+ int subnode_num = 0;
+
+ for_each_child_of_node(pdev->dev.of_node, node)
+ ++subnode_num;
+
+ num_smd_pkt_ports = subnode_num;
+
+ ret = smd_pkt_alloc_chrdev_region();
+ if (ret) {
+ pr_err("%s: smd_pkt_alloc_chrdev_region() failed ret:%i\n",
+ __func__, ret);
+ return ret;
+ }
+
+ for_each_child_of_node(pdev->dev.of_node, node) {
+ smd_pkt_devp = kzalloc(sizeof(struct smd_pkt_dev), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(smd_pkt_devp)) {
+ pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n",
+ __func__, i);
+ ret = -ENOMEM;
+ goto error_destroy;
+ }
+
+ ret = parse_smdpkt_devicetree(node, smd_pkt_devp);
+ if (ret) {
+ pr_err(" failed to parse_smdpkt_devicetree %d\n", i);
+ kfree(smd_pkt_devp);
+ goto error_destroy;
+ }
+
+ ret = smd_pkt_init_add_device(smd_pkt_devp, i);
+ if (ret < 0) {
+ pr_err("add device failed for idx:%d ret=%d\n", i, ret);
+ kfree(smd_pkt_devp);
+ goto error_destroy;
+ }
+ i++;
+ }
+
+ INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
+
+ D_STATUS("SMD Packet Port Driver Initialized.\n");
+ return 0;
+
+error_destroy:
+ smd_pkt_core_deinit();
+ return ret;
+}
+
+static int msm_smd_pkt_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (pdev) {
+ if (pdev->dev.of_node) {
+ D_STATUS("%s device tree implementation\n", __func__);
+ ret = smd_pkt_devicetree_init(pdev);
+ if (ret)
+ pr_err("%s: device tree init failed\n",
+ __func__);
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id msm_smd_pkt_match_table[] = {
+ { .compatible = "qcom,smdpkt" },
+ {},
+};
+
+static struct platform_driver msm_smd_pkt_driver = {
+ .probe = msm_smd_pkt_probe,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smd_pkt_match_table,
+ },
+};
+
+static int __init smd_pkt_init(void)
+{
+ int rc;
+
+ INIT_LIST_HEAD(&smd_pkt_dev_list);
+ INIT_LIST_HEAD(&smd_pkt_driver_list);
+ rc = platform_driver_register(&msm_smd_pkt_driver);
+ if (rc) {
+ pr_err("%s: msm_smd_driver register failed %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ smd_pkt_ilctxt = ipc_log_context_create(SMD_PKT_IPC_LOG_PAGE_CNT,
+ "smd_pkt", 0);
+ return 0;
+}
+
+static void __exit smd_pkt_cleanup(void)
+{
+ smd_pkt_core_deinit();
+}
+
+module_init(smd_pkt_init);
+module_exit(smd_pkt_cleanup);
+
+MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 65f7eecc45b0..f10a107614b4 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -401,7 +401,7 @@ static void disable_interrupts(struct tpm_chip *chip)
iowrite32(intmask,
chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.locality));
- free_irq(chip->vendor.irq, chip);
+ devm_free_irq(chip->pdev, chip->vendor.irq, chip);
chip->vendor.irq = 0;
}
diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c
index 19956f030ae9..adb07cdb7e8d 100644
--- a/drivers/clk/msm/clock-local2.c
+++ b/drivers/clk/msm/clock-local2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -928,7 +928,8 @@ static unsigned long branch_clk_get_rate(struct clk *c)
{
struct branch_clk *branch = to_branch_clk(c);
- if (branch->max_div)
+ if (branch->max_div ||
+ (branch->aggr_sibling_rates && !branch->is_prepared))
return branch->c.rate;
return clk_get_rate(c->parent);
diff --git a/drivers/clk/msm/clock-mmss-8998.c b/drivers/clk/msm/clock-mmss-8998.c
index 6ebb3ed6ed91..fdaaa723accd 100644
--- a/drivers/clk/msm/clock-mmss-8998.c
+++ b/drivers/clk/msm/clock-mmss-8998.c
@@ -359,6 +359,7 @@ static struct rcg_clk mdp_clk_src = {
.set_rate = set_rate_hid,
.freq_tbl = ftbl_mdp_clk_src,
.current_freq = &rcg_dummy_freq,
+ .non_local_children = true,
.base = &virt_base,
.c = {
.dbg_name = "mdp_clk_src",
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index f82ddc3b008b..8bf45f572c5e 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -84,12 +84,14 @@ enum clk_osm_trace_packet_id {
#define VERSION_REG 0x0
#define OSM_TABLE_SIZE 40
+#define MAX_VIRTUAL_CORNER (OSM_TABLE_SIZE - 1)
#define MAX_CLUSTER_CNT 2
#define LLM_SW_OVERRIDE_CNT 3
#define CORE_COUNT_VAL(val) ((val & GENMASK(18, 16)) >> 16)
#define SINGLE_CORE 1
#define MAX_CORE_COUNT 4
#define DEBUG_REG_NUM 3
+#define OSM_SEQ_MINUS_ONE 0xff
#define ENABLE_REG 0x1004
#define INDEX_REG 0x1150
@@ -353,8 +355,10 @@ struct clk_osm {
u32 cluster_num;
u32 irq;
u32 apm_crossover_vc;
+ u32 apm_threshold_pre_vc;
u32 apm_threshold_vc;
u32 mem_acc_crossover_vc;
+ u32 mem_acc_threshold_pre_vc;
u32 mem_acc_threshold_vc;
u32 cycle_counter_reads;
u32 cycle_counter_delay;
@@ -772,7 +776,7 @@ static const char * const gcc_parent_names_1[] = {
};
static struct freq_tbl ftbl_osm_clk_src[] = {
- F(200000000, LMH_LITE_CLK_SRC, 3, 0, 0),
+ F(200000000, LMH_LITE_CLK_SRC, 1.5, 0, 0),
{ }
};
@@ -1622,10 +1626,26 @@ static int clk_osm_resolve_crossover_corners(struct clk_osm *c,
if (corner_volt >= apm_threshold) {
c->apm_threshold_vc = c->osm_table[i].virtual_corner;
+ /*
+ * Handle case where VC 0 has open-loop
+ * greater than or equal to APM threshold voltage.
+ */
+ c->apm_threshold_pre_vc = c->apm_threshold_vc ?
+ c->apm_threshold_vc - 1 : OSM_SEQ_MINUS_ONE;
break;
}
}
+ /*
+ * This assumes the OSM table uses corners
+ * 0 to MAX_VIRTUAL_CORNER - 1.
+ */
+ if (!c->apm_threshold_vc &&
+ c->apm_threshold_pre_vc != OSM_SEQ_MINUS_ONE) {
+ c->apm_threshold_vc = MAX_VIRTUAL_CORNER;
+ c->apm_threshold_pre_vc = c->apm_threshold_vc - 1;
+ }
+
/* Determine MEM ACC threshold virtual corner */
if (mem_acc_threshold) {
for (i = 0; i < OSM_TABLE_SIZE; i++) {
@@ -1636,9 +1656,30 @@ static int clk_osm_resolve_crossover_corners(struct clk_osm *c,
if (corner_volt >= mem_acc_threshold) {
c->mem_acc_threshold_vc
= c->osm_table[i].virtual_corner;
+ /*
+ * Handle case where VC 0 has open-loop
+ * greater than or equal to MEM-ACC threshold
+ * voltage.
+ */
+ c->mem_acc_threshold_pre_vc =
+ c->mem_acc_threshold_vc ?
+ c->mem_acc_threshold_vc - 1 :
+ OSM_SEQ_MINUS_ONE;
break;
}
}
+
+ /*
+ * This assumes the OSM table uses corners
+ * 0 to MAX_VIRTUAL_CORNER - 1.
+ */
+ if (!c->mem_acc_threshold_vc && c->mem_acc_threshold_pre_vc
+ != OSM_SEQ_MINUS_ONE) {
+ c->mem_acc_threshold_vc =
+ MAX_VIRTUAL_CORNER;
+ c->mem_acc_threshold_pre_vc =
+ c->mem_acc_threshold_vc - 1;
+ }
}
return 0;
@@ -1989,13 +2030,20 @@ static void clk_osm_program_mem_acc_regs(struct clk_osm *c)
* highest MEM ACC threshold if it is specified instead of the
* fixed mapping in the LUT.
*/
- if (c->mem_acc_threshold_vc) {
- threshold_vc[2] = c->mem_acc_threshold_vc - 1;
+ if (c->mem_acc_threshold_vc || c->mem_acc_threshold_pre_vc
+ == OSM_SEQ_MINUS_ONE) {
+ threshold_vc[2] = c->mem_acc_threshold_pre_vc;
threshold_vc[3] = c->mem_acc_threshold_vc;
- if (threshold_vc[1] >= threshold_vc[2])
- threshold_vc[1] = threshold_vc[2] - 1;
- if (threshold_vc[0] >= threshold_vc[1])
- threshold_vc[0] = threshold_vc[1] - 1;
+
+ if (c->mem_acc_threshold_pre_vc == OSM_SEQ_MINUS_ONE) {
+ threshold_vc[1] = threshold_vc[0] =
+ c->mem_acc_threshold_pre_vc;
+ } else {
+ if (threshold_vc[1] >= threshold_vc[2])
+ threshold_vc[1] = threshold_vc[2] - 1;
+ if (threshold_vc[0] >= threshold_vc[1])
+ threshold_vc[0] = threshold_vc[1] - 1;
+ }
}
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(55),
@@ -2249,8 +2297,7 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c)
clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(1),
SEQ_REG(8));
clk_osm_write_reg(c, c->apm_threshold_vc, SEQ_REG(15));
- clk_osm_write_reg(c, c->apm_threshold_vc != 0 ?
- c->apm_threshold_vc - 1 : 0xff,
+ clk_osm_write_reg(c, c->apm_threshold_pre_vc,
SEQ_REG(31));
clk_osm_write_reg(c, 0x3b | c->apm_threshold_vc << 6,
SEQ_REG(73));
@@ -2268,8 +2315,7 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c)
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(15),
c->apm_threshold_vc);
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(31),
- c->apm_threshold_vc != 0 ?
- c->apm_threshold_vc - 1 : 0xff);
+ c->apm_threshold_pre_vc);
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(76),
0x39 | c->apm_threshold_vc << 6);
}
diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c
index b55310e091af..251acafca196 100644
--- a/drivers/clk/qcom/gcc-sdm660.c
+++ b/drivers/clk/qcom/gcc-sdm660.c
@@ -732,6 +732,7 @@ static struct clk_rcg2 gp3_clk_src = {
};
static const struct freq_tbl ftbl_hmss_gpll0_clk_src[] = {
+ F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
F(600000000, P_GPLL0_OUT_MAIN, 1, 0, 0),
{ }
};
@@ -2755,6 +2756,9 @@ static int gcc_660_probe(struct platform_device *pdev)
/* Keep bimc gfx clock port on all the time */
clk_prepare_enable(gcc_bimc_gfx_clk.clkr.hw.clk);
+ /* Set the HMSS_GPLL0_SRC for 300MHz to CPU subsystem */
+ clk_set_rate(hmss_gpll0_clk_src.clkr.hw.clk, 300000000);
+
dev_info(&pdev->dev, "Registered GCC clocks\n");
return ret;
@@ -2954,6 +2958,9 @@ static const char *const debug_mux_parent_names[] = {
"mmss_video_axi_clk",
"mmss_video_core_clk",
"mmss_video_subcore0_clk",
+ "mmss_throttle_camss_axi_clk",
+ "mmss_throttle_mdss_axi_clk",
+ "mmss_throttle_video_axi_clk",
"gpucc_gfx3d_clk",
"gpucc_rbbmtimer_clk",
"gpucc_rbcpr_clk",
@@ -3219,6 +3226,12 @@ static struct clk_debug_mux gcc_debug_mux = {
0x00E, 0, 0, 0x1000, BM(14, 13) },
{ "mmss_video_subcore0_clk", 0x22, MMCC,
0x01A, 0, 0, 0x1000, BM(14, 13) },
+ { "mmss_throttle_camss_axi_clk", 0x22, MMCC,
+ 0x0AA, 0, 0, 0x1000, BM(14, 13) },
+ { "mmss_throttle_mdss_axi_clk", 0x22, MMCC,
+ 0x0AB, 0, 0, 0x1000, BM(14, 13) },
+ { "mmss_throttle_video_axi_clk", 0x22, MMCC,
+ 0x0AC, 0, 0, 0x1000, BM(14, 13) },
{ "gpucc_gfx3d_clk", 0x13d, GPU,
0x008, 0, 0, 0, BM(18, 17) },
{ "gpucc_rbbmtimer_clk", 0x13d, GPU,
diff --git a/drivers/clk/qcom/mmcc-sdm660.c b/drivers/clk/qcom/mmcc-sdm660.c
index 910c36c65b6a..542737e4d204 100644
--- a/drivers/clk/qcom/mmcc-sdm660.c
+++ b/drivers/clk/qcom/mmcc-sdm660.c
@@ -2053,6 +2053,19 @@ static struct clk_branch mmss_camss_jpeg_axi_clk = {
},
};
+static struct clk_branch mmss_throttle_camss_axi_clk = {
+ .halt_reg = 0x3c3c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x3c3c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "mmss_throttle_camss_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
static struct clk_branch mmss_camss_mclk0_clk = {
.halt_reg = 0x3384,
.halt_check = BRANCH_HALT,
@@ -2341,6 +2354,19 @@ static struct clk_branch mmss_mdss_axi_clk = {
},
};
+static struct clk_branch mmss_throttle_mdss_axi_clk = {
+ .halt_reg = 0x246c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x246c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "mmss_throttle_mdss_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
static struct clk_branch mmss_mdss_byte0_clk = {
.halt_reg = 0x233c,
.halt_check = BRANCH_HALT,
@@ -2801,6 +2827,19 @@ static struct clk_branch mmss_video_axi_clk = {
},
};
+static struct clk_branch mmss_throttle_video_axi_clk = {
+ .halt_reg = 0x118c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x118c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "mmss_throttle_video_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
static struct clk_branch mmss_video_core_clk = {
.halt_reg = 0x1028,
.halt_check = BRANCH_HALT,
@@ -2962,6 +3001,9 @@ static struct clk_regmap *mmcc_660_clocks[] = {
[MMSS_MISC_CXO_CLK] = &mmss_misc_cxo_clk.clkr,
[MMSS_MNOC_AHB_CLK] = &mmss_mnoc_ahb_clk.clkr,
[MMSS_SNOC_DVM_AXI_CLK] = &mmss_snoc_dvm_axi_clk.clkr,
+ [MMSS_THROTTLE_CAMSS_AXI_CLK] = &mmss_throttle_camss_axi_clk.clkr,
+ [MMSS_THROTTLE_MDSS_AXI_CLK] = &mmss_throttle_mdss_axi_clk.clkr,
+ [MMSS_THROTTLE_VIDEO_AXI_CLK] = &mmss_throttle_video_axi_clk.clkr,
[MMSS_VIDEO_AHB_CLK] = &mmss_video_ahb_clk.clkr,
[MMSS_VIDEO_AXI_CLK] = &mmss_video_axi_clk.clkr,
[MMSS_VIDEO_CORE_CLK] = &mmss_video_core_clk.clkr,
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index e4d9aef1dda4..a0dba9beac05 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -698,9 +698,11 @@ static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy *policy,
char *buf)
{
unsigned int cur_freq = __cpufreq_get(policy);
- if (!cur_freq)
- return sprintf(buf, "<unknown>");
- return sprintf(buf, "%u\n", cur_freq);
+
+ if (cur_freq)
+ return sprintf(buf, "%u\n", cur_freq);
+
+ return sprintf(buf, "<unknown>\n");
}
/**
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 24ac49019b29..b91e115462ae 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -419,13 +419,13 @@ static u64 update_load(int cpu)
ppol->policy->governor_data;
u64 now;
u64 now_idle;
- unsigned int delta_idle;
- unsigned int delta_time;
+ u64 delta_idle;
+ u64 delta_time;
u64 active_time;
now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy);
- delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle);
- delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp);
+ delta_idle = (now_idle - pcpu->time_in_idle);
+ delta_time = (now - pcpu->time_in_idle_timestamp);
if (delta_time <= delta_idle)
active_time = 0;
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index ce67145bb142..4224b594f1b8 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -43,6 +43,7 @@
#include <soc/qcom/event_timer.h>
#include <soc/qcom/lpm-stats.h>
#include <soc/qcom/jtag.h>
+#include <soc/qcom/minidump.h>
#include <asm/cputype.h>
#include <asm/arch_timer.h>
#include <asm/cacheflush.h>
@@ -72,6 +73,8 @@ enum debug_event {
CLUSTER_ENTER,
CLUSTER_EXIT,
PRE_PC_CB,
+ CPU_HP_STARTING,
+ CPU_HP_DYING,
};
struct lpm_debug {
@@ -341,10 +344,16 @@ static int lpm_cpu_callback(struct notifier_block *cpu_nb,
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_DYING:
+ update_debug_pc_event(CPU_HP_DYING, cpu,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], false);
cluster_prepare(cluster, get_cpu_mask((unsigned int) cpu),
NR_LPM_LEVELS, false, 0);
break;
case CPU_STARTING:
+ update_debug_pc_event(CPU_HP_STARTING, cpu,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], false);
cluster_unprepare(cluster, get_cpu_mask((unsigned int) cpu),
NR_LPM_LEVELS, false, 0);
break;
@@ -682,7 +691,7 @@ static int cpu_power_select(struct cpuidle_device *dev,
if (!cpu)
return -EINVAL;
- if (sleep_disabled)
+ if (sleep_disabled && !cpu_isolated(dev->cpu))
return 0;
idx_restrict = cpu->nlevels + 1;
@@ -1846,6 +1855,7 @@ static int lpm_probe(struct platform_device *pdev)
int ret;
int size;
struct kobject *module_kobj = NULL;
+ struct md_region md_entry;
get_online_cpus();
lpm_root_node = lpm_of_parse_cluster(pdev);
@@ -1906,6 +1916,14 @@ static int lpm_probe(struct platform_device *pdev)
goto failed;
}
+ /* Add lpm_debug to Minidump*/
+ strlcpy(md_entry.name, "KLPMDEBUG", sizeof(md_entry.name));
+ md_entry.virt_addr = (uintptr_t)lpm_debug;
+ md_entry.phys_addr = lpm_debug_phys;
+ md_entry.size = size;
+ if (msm_minidump_add_region(&md_entry))
+ pr_info("Failed to add lpm_debug in Minidump\n");
+
return 0;
failed:
free_cluster_node(lpm_root_node);
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 7459401979fe..d04ca6f28f90 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -56,6 +56,7 @@ static uint8_t _std_init_vector_sha256_uint8[] = {
static DEFINE_MUTEX(send_cmd_lock);
static DEFINE_MUTEX(qcedev_sent_bw_req);
+static DEFINE_MUTEX(hash_access_lock);
static void qcedev_ce_high_bw_req(struct qcedev_control *podev,
bool high_bw_req)
@@ -1648,12 +1649,18 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
(void __user *)arg,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
- if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+ mutex_lock(&hash_access_lock);
+ if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) {
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
+ }
qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
err = qcedev_hash_init(&qcedev_areq, handle, &sg_src);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
+ mutex_unlock(&hash_access_lock);
if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
@@ -1671,32 +1678,42 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
(void __user *)arg,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
- if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+ mutex_lock(&hash_access_lock);
+ if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) {
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
+ }
qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
if (qcedev_areq.sha_op_req.alg == QCEDEV_ALG_AES_CMAC) {
err = qcedev_hash_cmac(&qcedev_areq, handle, &sg_src);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
} else {
if (handle->sha_ctxt.init_done == false) {
pr_err("%s Init was not called\n", __func__);
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
}
err = qcedev_hash_update(&qcedev_areq, handle, &sg_src);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
}
if (handle->sha_ctxt.diglen > QCEDEV_MAX_SHA_DIGEST) {
pr_err("Invalid sha_ctxt.diglen %d\n",
handle->sha_ctxt.diglen);
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
}
memcpy(&qcedev_areq.sha_op_req.digest[0],
&handle->sha_ctxt.digest[0],
handle->sha_ctxt.diglen);
+ mutex_unlock(&hash_access_lock);
if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
@@ -1713,16 +1730,22 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
(void __user *)arg,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
- if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+ mutex_lock(&hash_access_lock);
+ if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) {
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
+ }
qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
err = qcedev_hash_final(&qcedev_areq, handle);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
qcedev_areq.sha_op_req.diglen = handle->sha_ctxt.diglen;
memcpy(&qcedev_areq.sha_op_req.digest[0],
&handle->sha_ctxt.digest[0],
handle->sha_ctxt.diglen);
+ mutex_unlock(&hash_access_lock);
if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
@@ -1737,20 +1760,28 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
(void __user *)arg,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
- if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+ mutex_lock(&hash_access_lock);
+ if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) {
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
+ }
qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
qcedev_hash_init(&qcedev_areq, handle, &sg_src);
err = qcedev_hash_update(&qcedev_areq, handle, &sg_src);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
err = qcedev_hash_final(&qcedev_areq, handle);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
qcedev_areq.sha_op_req.diglen = handle->sha_ctxt.diglen;
memcpy(&qcedev_areq.sha_op_req.digest[0],
&handle->sha_ctxt.digest[0],
handle->sha_ctxt.diglen);
+ mutex_unlock(&hash_access_lock);
if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c
index ffb2237da5fa..bbe1d24fb1f6 100644
--- a/drivers/esoc/esoc_dev.c
+++ b/drivers/esoc/esoc_dev.c
@@ -338,7 +338,6 @@ int esoc_clink_del_device(struct device *dev, void *dummy)
esoc_udev = esoc_udev_get_by_minor(esoc_clink->id);
if (!esoc_udev)
return 0;
- return_esoc_udev(esoc_udev);
device_destroy(esoc_class, MKDEV(esoc_major, esoc_clink->id));
return_esoc_udev(esoc_udev);
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
index 7c42ff670080..a0924330d125 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
@@ -25,6 +25,7 @@
* Alex Deucher
* Jerome Glisse
*/
+#include <linux/irq.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/amdgpu_drm.h>
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index cc1e16fd7e76..02a60a1df50d 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -89,6 +89,14 @@ struct detailed_mode_closure {
#define LEVEL_GTF2 2
#define LEVEL_CVT 3
+/*Enum storing luminance types for HDR blocks in EDID*/
+enum luminance_value {
+ NO_LUMINANCE_DATA = 3,
+ MAXIMUM_LUMINANCE = 4,
+ FRAME_AVERAGE_LUMINANCE = 5,
+ MINIMUM_LUMINANCE = 6
+};
+
static struct edid_quirk {
char vendor[4];
int product_id;
@@ -992,6 +1000,221 @@ static const struct drm_display_mode edid_cea_modes[] = {
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ /* 65 - 1280x720@24Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 66 - 1280x720@25Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
+ 3740, 3960, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 67 - 1280x720@30Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 68 - 1280x720@50Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 69 - 1280x720@60Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 70 - 1280x720@100Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 71 - 1280x720@120Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 72 - 1920x1080@24Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 73 - 1920x1080@25Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 74 - 1920x1080@30Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 75 - 1920x1080@50Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 76 - 1920x1080@60Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 77 - 1920x1080@100Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 78 - 1920x1080@120Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 79 - 1680x720@24Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 80 - 1680x720@25Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908,
+ 2948, 3168, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 81 - 1680x720@30Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380,
+ 2420, 2640, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 82 - 1680x720@50Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940,
+ 1980, 2200, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 83 - 1680x720@60Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940,
+ 1980, 2200, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 84 - 1680x720@100Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740,
+ 1780, 2000, 0, 720, 725, 730, 825, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 85 - 1680x720@120Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740,
+ 1780, 2000, 0, 720, 725, 730, 825, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 86 - 2560x1080@24Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558,
+ 3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 87 - 2560x1080@25Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008,
+ 3052, 3200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 88 - 2560x1080@30Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328,
+ 3372, 3520, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 89 - 2560x1080@50Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108,
+ 3152, 3300, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 90 - 2560x1080@60Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808,
+ 2852, 3000, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 91 - 2560x1080@100Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778,
+ 2822, 2970, 0, 1080, 1084, 1089, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 92 - 2560x1080@120Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108,
+ 3152, 3300, 0, 1080, 1084, 1089, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 93 - 3840x2160p@24Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,},
+ /* 94 - 3840x2160p@25Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 95 - 3840x2160p@30Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 96 - 3840x2160p@50Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 97 - 3840x2160p@60Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 98 - 4096x2160p@24Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 99 - 4096x2160p@25Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064,
+ 5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 100 - 4096x2160p@30Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184,
+ 4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 101 - 4096x2160p@50Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064,
+ 5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 102 - 4096x2160p@60Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184,
+ 4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 103 - 3840x2160p@24Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 104 - 3840x2160p@25Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 105 - 3840x2160p@30Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 106 - 3840x2160p@50Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 107 - 3840x2160p@60Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
};
/*
@@ -2482,12 +2705,15 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
return closure.modes;
}
-
+#define VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK 0x0
#define AUDIO_BLOCK 0x01
#define VIDEO_BLOCK 0x02
#define VENDOR_BLOCK 0x03
#define SPEAKER_BLOCK 0x04
+#define HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK 0x06
+#define EXTENDED_TAG 0x07
#define VIDEO_CAPABILITY_BLOCK 0x07
+#define Y420_VIDEO_DATA_BLOCK 0x0E
#define EDID_BASIC_AUDIO (1 << 6)
#define EDID_CEA_YCRCB444 (1 << 5)
#define EDID_CEA_YCRCB422 (1 << 4)
@@ -3076,6 +3302,21 @@ static bool cea_db_is_hdmi_vsdb(const u8 *db)
return hdmi_id == HDMI_IEEE_OUI;
}
+static bool cea_db_is_hdmi_hf_vsdb(const u8 *db)
+{
+ int hdmi_id;
+
+ if (cea_db_tag(db) != VENDOR_BLOCK)
+ return false;
+
+ if (cea_db_payload_len(db) < 7)
+ return false;
+
+ hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
+
+ return hdmi_id == HDMI_IEEE_OUI_HF;
+}
+
#define for_each_cea_db(cea, i, start, end) \
for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
@@ -3199,6 +3440,258 @@ parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db)
}
static void
+parse_hdmi_hf_vsdb(struct drm_connector *connector, const u8 *db)
+{
+ u8 len = cea_db_payload_len(db);
+
+ if (len < 7)
+ return;
+
+ if (db[4] != 1)
+ return; /* invalid version */
+
+ connector->max_tmds_char = db[5] * 5;
+ connector->scdc_present = db[6] & (1 << 7);
+ connector->rr_capable = db[6] & (1 << 6);
+ connector->flags_3d = db[6] & 0x7;
+ connector->supports_scramble = connector->scdc_present &&
+ (db[6] & (1 << 3));
+
+ DRM_DEBUG_KMS("HDMI v2: max TMDS char %d, "
+ "scdc %s, "
+ "rr %s, "
+ "3D flags 0x%x, "
+ "scramble %s\n",
+ connector->max_tmds_char,
+ connector->scdc_present ? "available" : "not available",
+ connector->rr_capable ? "capable" : "not capable",
+ connector->flags_3d,
+ connector->supports_scramble ?
+ "supported" : "not supported");
+}
+
+static void
+drm_hdmi_extract_vsdbs_info(struct drm_connector *connector, struct edid *edid)
+{
+ const u8 *cea = drm_find_cea_extension(edid);
+ const u8 *db = NULL;
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+
+ if (cea_db_tag(db) == VENDOR_BLOCK) {
+ /* HDMI Vendor-Specific Data Block */
+ if (cea_db_is_hdmi_vsdb(db))
+ parse_hdmi_vsdb(connector, db);
+ /* HDMI Forum Vendor-Specific Data Block */
+ else if (cea_db_is_hdmi_hf_vsdb(db))
+ parse_hdmi_hf_vsdb(connector, db);
+ }
+ }
+ }
+}
+
+/*
+ * drm_extract_vcdb_info - Parse the HDMI Video Capability Data Block
+ * @connector: connector corresponding to the HDMI sink
+ * @db: start of the CEA vendor specific block
+ *
+ * Parses the HDMI VCDB to extract sink info for @connector.
+ */
+static void
+drm_extract_vcdb_info(struct drm_connector *connector, const u8 *db)
+{
+ /*
+ * Check if the sink specifies underscan
+ * support for:
+ * BIT 5: preferred video format
+ * BIT 3: IT video format
+ * BIT 1: CE video format
+ */
+
+ connector->pt_scan_info =
+ (db[2] & (BIT(4) | BIT(5))) >> 4;
+ connector->it_scan_info =
+ (db[2] & (BIT(3) | BIT(2))) >> 2;
+ connector->ce_scan_info =
+ db[2] & (BIT(1) | BIT(0));
+
+ DRM_DEBUG_KMS("Scan Info (pt|it|ce): (%d|%d|%d)",
+ (int) connector->pt_scan_info,
+ (int) connector->it_scan_info,
+ (int) connector->ce_scan_info);
+}
+
+static bool drm_edid_is_luminance_value_present(
+u32 block_length, enum luminance_value value)
+{
+ return block_length > NO_LUMINANCE_DATA && value <= block_length;
+}
+
+/*
+ * drm_extract_hdr_db - Parse the HDMI HDR extended block
+ * @connector: connector corresponding to the HDMI sink
+ * @db: start of the HDMI HDR extended block
+ *
+ * Parses the HDMI HDR extended block to extract sink info for @connector.
+ */
+static void
+drm_extract_hdr_db(struct drm_connector *connector, const u8 *db)
+{
+
+ u8 len = 0;
+
+ if (!db) {
+ DRM_ERROR("invalid db\n");
+ return;
+ }
+
+ len = db[0] & 0x1f;
+ /* Byte 3: Electro-Optical Transfer Functions */
+ connector->hdr_eotf = db[2] & 0x3F;
+
+ /* Byte 4: Static Metadata Descriptor Type 1 */
+ connector->hdr_metadata_type_one = (db[3] & BIT(0));
+
+ /* Byte 5: Desired Content Maximum Luminance */
+ if (drm_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE))
+ connector->hdr_max_luminance =
+ db[MAXIMUM_LUMINANCE];
+
+ /* Byte 6: Desired Content Max Frame-average Luminance */
+ if (drm_edid_is_luminance_value_present(len, FRAME_AVERAGE_LUMINANCE))
+ connector->hdr_avg_luminance =
+ db[FRAME_AVERAGE_LUMINANCE];
+
+ /* Byte 7: Desired Content Min Luminance */
+ if (drm_edid_is_luminance_value_present(len, MINIMUM_LUMINANCE))
+ connector->hdr_min_luminance =
+ db[MINIMUM_LUMINANCE];
+
+ connector->hdr_supported = true;
+
+ DRM_DEBUG_KMS("HDR electro-optical %d\n", connector->hdr_eotf);
+ DRM_DEBUG_KMS("metadata desc 1 %d\n", connector->hdr_metadata_type_one);
+ DRM_DEBUG_KMS("max luminance %d\n", connector->hdr_max_luminance);
+ DRM_DEBUG_KMS("avg luminance %d\n", connector->hdr_avg_luminance);
+ DRM_DEBUG_KMS("min luminance %d\n", connector->hdr_min_luminance);
+}
+
+/*
+ * drm_hdmi_extract_extended_blk_info - Parse the HDMI extended tag blocks
+ * @connector: connector corresponding to the HDMI sink
+ * @edid: handle to the EDID structure
+ * Parses the all extended tag blocks extract sink info for @connector.
+ */
+static void
+drm_hdmi_extract_extended_blk_info(struct drm_connector *connector,
+struct edid *edid)
+{
+ const u8 *cea = drm_find_cea_extension(edid);
+ const u8 *db = NULL;
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+
+ if (cea_db_tag(db) == EXTENDED_TAG) {
+ DRM_DEBUG_KMS("found extended tag block = %d\n",
+ db[1]);
+ switch (db[1]) {
+ case VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK:
+ drm_extract_vcdb_info(connector, db);
+ break;
+ case HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK:
+ drm_extract_hdr_db(connector, db);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+static u8 *
+drm_edid_find_extended_tag_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid) {
+ pr_err("%s: invalid input\n", __func__);
+ return NULL;
+ }
+
+ cea = drm_find_cea_extension(edid);
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return NULL;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if ((cea_db_tag(db) == EXTENDED_TAG) &&
+ (db[1] == blk_id))
+ return db;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * add_YCbCr420VDB_modes - add the modes found in Ycbcr420 VDB block
+ * @connector: connector corresponding to the HDMI sink
+ * @edid: handle to the EDID structure
+ * Parses the YCbCr420 VDB block and adds the modes to @connector.
+ */
+static int
+add_YCbCr420VDB_modes(struct drm_connector *connector, struct edid *edid)
+{
+
+ const u8 *db = NULL;
+ u32 i = 0;
+ u32 modes = 0;
+ u32 video_format = 0;
+ u8 len = 0;
+
+ /*Find the YCbCr420 VDB*/
+ db = drm_edid_find_extended_tag_block(edid, Y420_VIDEO_DATA_BLOCK);
+ /* Offset to byte 3 */
+ if (db) {
+ len = db[0] & 0x1F;
+ db += 2;
+ for (i = 0; i < len - 1; i++) {
+ struct drm_display_mode *mode;
+
+ video_format = *(db + i) & 0x7F;
+ mode = drm_display_mode_from_vic_index(connector,
+ db, len-1, i);
+ if (mode) {
+ DRM_DEBUG_KMS("Adding mode for vic = %d\n",
+ video_format);
+ drm_mode_probed_add(connector, mode);
+ modes++;
+ }
+ }
+ }
+ return modes;
+}
+
+static void
monitor_name(struct detailed_timing *t, void *data)
{
if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
@@ -3277,6 +3770,9 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
/* HDMI Vendor-Specific Data Block */
if (cea_db_is_hdmi_vsdb(db))
parse_hdmi_vsdb(connector, db);
+ /* HDMI Forum Vendor-Specific Data Block */
+ else if (cea_db_is_hdmi_hf_vsdb(db))
+ parse_hdmi_hf_vsdb(connector, db);
break;
default:
break;
@@ -3733,6 +4229,10 @@ static void drm_add_display_info(struct edid *edid,
info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
}
+ /* Extract audio and video latency fields for the sink */
+ drm_hdmi_extract_vsdbs_info(connector, edid);
+ /* Extract info from extended tag blocks */
+ drm_hdmi_extract_extended_blk_info(connector, edid);
/* HDMI deep color modes supported? Assign to info, if so */
drm_assign_hdmi_deep_color_info(edid, info, connector);
@@ -3775,6 +4275,148 @@ static void drm_add_display_info(struct edid *edid,
info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
}
+static int validate_displayid(u8 *displayid, int length, int idx)
+{
+ int i;
+ u8 csum = 0;
+ struct displayid_hdr *base;
+
+ base = (struct displayid_hdr *)&displayid[idx];
+
+ DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n",
+ base->rev, base->bytes, base->prod_id, base->ext_count);
+
+ if (base->bytes + 5 > length - idx)
+ return -EINVAL;
+ for (i = idx; i <= base->bytes + 5; i++)
+ csum += displayid[i];
+
+ if (csum) {
+ DRM_ERROR("DisplayID checksum invalid, remainder is %d\n",
+ csum);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct drm_display_mode *
+drm_mode_displayid_detailed(struct drm_device *dev,
+struct displayid_detailed_timings_1 *timings)
+{
+ struct drm_display_mode *mode;
+ unsigned pixel_clock = (timings->pixel_clock[0] |
+ (timings->pixel_clock[1] << 8) |
+ (timings->pixel_clock[2] << 16));
+ unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1;
+ unsigned hblank =
+ (timings->hblank[0] |
+ timings->hblank[1] << 8) + 1;
+ unsigned hsync = (timings->hsync[0] |
+ (timings->hsync[1] & 0x7f) << 8) + 1;
+ unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1;
+ unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1;
+ unsigned vblank =
+ (timings->vblank[0] |
+ timings->vblank[1] << 8) + 1;
+ unsigned vsync =
+ (timings->vsync[0] |
+ (timings->vsync[1] & 0x7f) << 8) + 1;
+ unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1;
+ bool hsync_positive = (timings->hsync[1] >> 7) & 0x1;
+ bool vsync_positive = (timings->vsync[1] >> 7) & 0x1;
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ mode->clock = pixel_clock * 10;
+ mode->hdisplay = hactive;
+ mode->hsync_start = mode->hdisplay + hsync;
+ mode->hsync_end = mode->hsync_start + hsync_width;
+ mode->htotal = mode->hdisplay + hblank;
+
+ mode->vdisplay = vactive;
+ mode->vsync_start = mode->vdisplay + vsync;
+ mode->vsync_end = mode->vsync_start + vsync_width;
+ mode->vtotal = mode->vdisplay + vblank;
+
+ mode->flags = 0;
+ mode->flags |= hsync_positive ?
+ DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+ mode->flags |= vsync_positive ?
+ DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
+ mode->type = DRM_MODE_TYPE_DRIVER;
+
+ if (timings->flags & 0x80)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ mode->vrefresh = drm_mode_vrefresh(mode);
+ drm_mode_set_name(mode);
+
+ return mode;
+}
+
+static int add_displayid_detailed_1_modes(struct drm_connector *connector,
+ struct displayid_block *block)
+{
+ struct displayid_detailed_timing_block *det =
+ (struct displayid_detailed_timing_block *)block;
+ int i;
+ int num_timings;
+ struct drm_display_mode *newmode;
+ int num_modes = 0;
+ /* blocks must be multiple of 20 bytes length */
+ if (block->num_bytes % 20)
+ return 0;
+
+ num_timings = block->num_bytes / 20;
+ for (i = 0; i < num_timings; i++) {
+ struct displayid_detailed_timings_1 *timings = &det->timings[i];
+
+ newmode = drm_mode_displayid_detailed(connector->dev, timings);
+ if (!newmode)
+ continue;
+
+ drm_mode_probed_add(connector, newmode);
+ num_modes++;
+ }
+ return num_modes;
+}
+
+static int add_displayid_detailed_modes(struct drm_connector *connector,
+ struct edid *edid)
+{
+ u8 *displayid;
+ int ret;
+ int idx = 1;
+ int length = EDID_LENGTH;
+ struct displayid_block *block;
+ int num_modes = 0;
+
+ displayid = drm_find_displayid_extension(edid);
+ if (!displayid)
+ return 0;
+
+ ret = validate_displayid(displayid, length, idx);
+ if (ret)
+ return 0;
+
+ idx += sizeof(struct displayid_hdr);
+ while (block = (struct displayid_block *)&displayid[idx],
+ idx + sizeof(struct displayid_block) <= length &&
+ idx + sizeof(struct displayid_block) +
+ block->num_bytes <= length &&
+ block->num_bytes > 0) {
+ idx += block->num_bytes + sizeof(struct displayid_block);
+ switch (block->tag) {
+ case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
+ num_modes += add_displayid_detailed_1_modes(connector,
+ block);
+ break;
+ }
+ }
+ return num_modes;
+}
+
/**
* drm_add_edid_modes - add modes from EDID data, if available
* @connector: connector we're probing
@@ -3820,6 +4462,8 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
num_modes += add_established_modes(connector, edid);
num_modes += add_cea_modes(connector, edid);
num_modes += add_alternate_cea_modes(connector, edid);
+ num_modes += add_displayid_detailed_modes(connector, edid);
+ num_modes += add_YCbCr420VDB_modes(connector, edid);
if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
num_modes += add_inferred_modes(connector, edid);
@@ -4029,96 +4673,105 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
}
EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);
+static int drm_parse_tiled_block(struct drm_connector *connector,
+ struct displayid_block *block)
+{
+ struct displayid_tiled_block *tile =
+ (struct displayid_tiled_block *)block;
+ u16 w, h;
+ u8 tile_v_loc, tile_h_loc;
+ u8 num_v_tile, num_h_tile;
+ struct drm_tile_group *tg;
+
+ w = tile->tile_size[0] | tile->tile_size[1] << 8;
+ h = tile->tile_size[2] | tile->tile_size[3] << 8;
+
+ num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30);
+ num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30);
+ tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4);
+ tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4);
+
+ connector->has_tile = true;
+ if (tile->tile_cap & 0x80)
+ connector->tile_is_single_monitor = true;
+
+ connector->num_h_tile = num_h_tile + 1;
+ connector->num_v_tile = num_v_tile + 1;
+ connector->tile_h_loc = tile_h_loc;
+ connector->tile_v_loc = tile_v_loc;
+ connector->tile_h_size = w + 1;
+ connector->tile_v_size = h + 1;
+
+ DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap);
+ DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1);
+ DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n",
+ num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc);
+ DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0],
+ tile->topology_id[1], tile->topology_id[2]);
+
+ tg = drm_mode_get_tile_group(connector->dev, tile->topology_id);
+ if (!tg)
+ tg = drm_mode_create_tile_group(connector->dev,
+ tile->topology_id);
+
+ if (!tg)
+ return -ENOMEM;
+
+ if (connector->tile_group != tg) {
+ /* if we haven't got a pointer,
+ * take the reference, drop ref to old tile group
+ */
+ if (connector->tile_group)
+ drm_mode_put_tile_group(connector->dev,
+ connector->tile_group);
+
+ connector->tile_group = tg;
+ } else
+ /* if same tile group, then release the ref we just took. */
+ drm_mode_put_tile_group(connector->dev, tg);
+ return 0;
+}
+
static int drm_parse_display_id(struct drm_connector *connector,
u8 *displayid, int length,
bool is_edid_extension)
{
/* if this is an EDID extension the first byte will be 0x70 */
int idx = 0;
- struct displayid_hdr *base;
struct displayid_block *block;
- u8 csum = 0;
- int i;
+ int ret;
if (is_edid_extension)
idx = 1;
- base = (struct displayid_hdr *)&displayid[idx];
-
- DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n",
- base->rev, base->bytes, base->prod_id, base->ext_count);
-
- if (base->bytes + 5 > length - idx)
- return -EINVAL;
-
- for (i = idx; i <= base->bytes + 5; i++) {
- csum += displayid[i];
- }
- if (csum) {
- DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", csum);
- return -EINVAL;
- }
+ ret = validate_displayid(displayid, length, idx);
+ if (ret)
+ return ret;
- block = (struct displayid_block *)&displayid[idx + 4];
- DRM_DEBUG_KMS("block id %d, rev %d, len %d\n",
- block->tag, block->rev, block->num_bytes);
-
- switch (block->tag) {
- case DATA_BLOCK_TILED_DISPLAY: {
- struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block;
-
- u16 w, h;
- u8 tile_v_loc, tile_h_loc;
- u8 num_v_tile, num_h_tile;
- struct drm_tile_group *tg;
-
- w = tile->tile_size[0] | tile->tile_size[1] << 8;
- h = tile->tile_size[2] | tile->tile_size[3] << 8;
-
- num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30);
- num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30);
- tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4);
- tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4);
-
- connector->has_tile = true;
- if (tile->tile_cap & 0x80)
- connector->tile_is_single_monitor = true;
-
- connector->num_h_tile = num_h_tile + 1;
- connector->num_v_tile = num_v_tile + 1;
- connector->tile_h_loc = tile_h_loc;
- connector->tile_v_loc = tile_v_loc;
- connector->tile_h_size = w + 1;
- connector->tile_v_size = h + 1;
-
- DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap);
- DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1);
- DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n",
- num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc);
- DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]);
-
- tg = drm_mode_get_tile_group(connector->dev, tile->topology_id);
- if (!tg) {
- tg = drm_mode_create_tile_group(connector->dev, tile->topology_id);
+ idx += sizeof(struct displayid_hdr);
+ while (block = (struct displayid_block *)&displayid[idx],
+ idx + sizeof(struct displayid_block) <= length &&
+ idx + sizeof(struct displayid_block) +
+ block->num_bytes <= length &&
+ block->num_bytes > 0) {
+ idx += block->num_bytes + sizeof(struct displayid_block);
+ DRM_DEBUG_KMS("block id 0x%x, rev %d, len %d\n",
+ block->tag, block->rev, block->num_bytes);
+
+ switch (block->tag) {
+ case DATA_BLOCK_TILED_DISPLAY:
+ ret = drm_parse_tiled_block(connector, block);
+ if (ret)
+ return ret;
+ break;
+ case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
+ /* handled in mode gathering code. */
+ break;
+ default:
+ DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n",
+ block->tag);
+ break;
}
- if (!tg)
- return -ENOMEM;
-
- if (connector->tile_group != tg) {
- /* if we haven't got a pointer,
- take the reference, drop ref to old tile group */
- if (connector->tile_group) {
- drm_mode_put_tile_group(connector->dev, connector->tile_group);
- }
- connector->tile_group = tg;
- } else
- /* if same tile group, then release the ref we just took. */
- drm_mode_put_tile_group(connector->dev, tg);
- }
- break;
- default:
- printk("unknown displayid tag %d\n", block->tag);
- break;
}
return 0;
}
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index ebf8be80a3d9..3d0617dbc514 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -49,6 +49,7 @@ msm_drm-y := \
sde/sde_vbif.o \
sde_dbg_evtlog.o \
sde_io_util.o \
+ sde_edid_parser.o
# use drm gpu driver only if qcom_kgsl driver not available
ifneq ($(CONFIG_QCOM_KGSL),y)
@@ -102,7 +103,6 @@ msm_drm-$(CONFIG_DRM_SDE_HDMI) += \
hdmi-staging/sde_hdmi.o \
hdmi-staging/sde_hdmi_bridge.o \
hdmi-staging/sde_hdmi_audio.o \
- hdmi-staging/sde_hdmi_edid.o
msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
dsi/pll/dsi_pll_28nm.o
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 5b2c7e77771c..9ceef8f437b5 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -133,10 +133,30 @@ static int a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_PKT7(ring, CP_YIELD_ENABLE, 1);
OUT_RING(ring, 0x02);
+ /* Record the always on counter before command execution */
+ if (submit->profile_buf_iova) {
+ uint64_t gpuaddr = submit->profile_buf_iova +
+ offsetof(struct drm_msm_gem_submit_profile_buffer,
+ ticks_submitted);
+
+ /*
+ * Set bit[30] to make this command a 64 bit write operation.
+ * bits[18-29] is to specify number of consecutive registers
+ * to copy, so set this space with 2, since we want to copy
+ * data from REG_A5XX_RBBM_ALWAYSON_COUNTER_LO and [HI].
+ */
+ OUT_PKT7(ring, CP_REG_TO_MEM, 3);
+ OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO |
+ (1 << 30) | (2 << 18));
+ OUT_RING(ring, lower_32_bits(gpuaddr));
+ OUT_RING(ring, upper_32_bits(gpuaddr));
+ }
+
/* Submit the commands */
for (i = 0; i < submit->nr_cmds; i++) {
switch (submit->cmd[i].type) {
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
+ case MSM_SUBMIT_CMD_PROFILE_BUF:
break;
case MSM_SUBMIT_CMD_BUF:
OUT_PKT7(ring, CP_INDIRECT_BUFFER_PFE, 3);
@@ -164,6 +184,19 @@ static int a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_PKT7(ring, CP_YIELD_ENABLE, 1);
OUT_RING(ring, 0x01);
+ /* Record the always on counter after command execution */
+ if (submit->profile_buf_iova) {
+ uint64_t gpuaddr = submit->profile_buf_iova +
+ offsetof(struct drm_msm_gem_submit_profile_buffer,
+ ticks_retired);
+
+ OUT_PKT7(ring, CP_REG_TO_MEM, 3);
+ OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO |
+ (1 << 30) | (2 << 18));
+ OUT_RING(ring, lower_32_bits(gpuaddr));
+ OUT_RING(ring, upper_32_bits(gpuaddr));
+ }
+
/* Write the fence to the scratch register */
OUT_PKT4(ring, REG_A5XX_CP_SCRATCH_REG(2), 1);
OUT_RING(ring, submit->fence);
@@ -193,6 +226,35 @@ static int a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
/* Set bit 0 to trigger an interrupt on preempt complete */
OUT_RING(ring, 0x01);
+ if (submit->profile_buf_iova) {
+ unsigned long flags;
+ uint64_t ktime;
+ struct drm_msm_gem_submit_profile_buffer *profile_buf =
+ submit->profile_buf_vaddr;
+
+ /*
+ * With this profiling, we are trying to create closest
+ * possible mapping between the CPU time domain(monotonic clock)
+ * and the GPU time domain(ticks). In order to make this
+ * happen, we need to briefly turn off interrupts to make sure
+ * interrupts do not run between collecting these two samples.
+ */
+ local_irq_save(flags);
+
+ profile_buf->ticks_queued = gpu_read64(gpu,
+ REG_A5XX_RBBM_ALWAYSON_COUNTER_LO,
+ REG_A5XX_RBBM_ALWAYSON_COUNTER_HI);
+
+ ktime = ktime_get_raw_ns();
+
+ local_irq_restore(flags);
+
+ do_div(ktime, NSEC_PER_SEC);
+
+ profile_buf->queue_time = ktime;
+ profile_buf->submit_time = ktime;
+ }
+
a5xx_flush(gpu, ring);
/* Check to see if we need to start preemption */
@@ -1039,8 +1101,10 @@ static irqreturn_t a5xx_irq(struct msm_gpu *gpu)
if (status & A5XX_RBBM_INT_0_MASK_GPMU_VOLTAGE_DROOP)
a5xx_gpmu_err_irq(gpu);
- if (status & A5XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS)
+ if (status & A5XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS) {
+ a5xx_preempt_trigger(gpu);
msm_gpu_retire(gpu);
+ }
if (status & A5XX_RBBM_INT_0_MASK_CP_SW)
a5xx_preempt_irq(gpu);
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 969ed810ce9d..19267b2a3b49 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -183,6 +183,7 @@ int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
/* ignore IB-targets */
break;
+ case MSM_SUBMIT_CMD_PROFILE_BUF:
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
break;
case MSM_SUBMIT_CMD_BUF:
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 347b78886b24..6a6d02c5444d 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -32,6 +32,22 @@
static DEFINE_MUTEX(sde_hdmi_list_lock);
static LIST_HEAD(sde_hdmi_list);
+/* HDMI SCDC register offsets */
+#define HDMI_SCDC_UPDATE_0 0x10
+#define HDMI_SCDC_UPDATE_1 0x11
+#define HDMI_SCDC_TMDS_CONFIG 0x20
+#define HDMI_SCDC_SCRAMBLER_STATUS 0x21
+#define HDMI_SCDC_CONFIG_0 0x30
+#define HDMI_SCDC_STATUS_FLAGS_0 0x40
+#define HDMI_SCDC_STATUS_FLAGS_1 0x41
+#define HDMI_SCDC_ERR_DET_0_L 0x50
+#define HDMI_SCDC_ERR_DET_0_H 0x51
+#define HDMI_SCDC_ERR_DET_1_L 0x52
+#define HDMI_SCDC_ERR_DET_1_H 0x53
+#define HDMI_SCDC_ERR_DET_2_L 0x54
+#define HDMI_SCDC_ERR_DET_2_H 0x55
+#define HDMI_SCDC_ERR_DET_CHECKSUM 0x56
+
static const struct of_device_id sde_hdmi_dt_match[] = {
{.compatible = "qcom,hdmi-display"},
{}
@@ -69,16 +85,328 @@ static ssize_t _sde_hdmi_debugfs_dump_info_read(struct file *file,
return len;
}
+static ssize_t _sde_hdmi_debugfs_edid_modes_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char *buf;
+ u32 len = 0;
+ struct drm_connector *connector;
+ u32 mode_count = 0;
+ struct drm_display_mode *mode;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl ||
+ !display->ctrl.ctrl->connector) {
+ SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n",
+ display);
+ return -ENOMEM;
+ }
+
+ if (*ppos)
+ return 0;
+
+ connector = display->ctrl.ctrl->connector;
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ mode_count++;
+ }
+
+ /* Adding one more to store title */
+ mode_count++;
+
+ buf = kzalloc((mode_count * sizeof(*mode)), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "name refresh (Hz) hdisp hss hse htot vdisp");
+
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ " vss vse vtot flags\n");
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ len += snprintf(buf + len, SZ_4K - len,
+ "%s %d %d %d %d %d %d %d %d %d 0x%x\n",
+ mode->name, mode->vrefresh, mode->hdisplay,
+ mode->hsync_start, mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start, mode->vsync_end,
+ mode->vtotal, mode->flags);
+ }
+
+ if (copy_to_user(buff, buf, len)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ *ppos += len;
+
+ kfree(buf);
+ return len;
+}
+
+static ssize_t _sde_hdmi_debugfs_edid_vsdb_info_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[200];
+ u32 len = 0;
+ struct drm_connector *connector;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl ||
+ !display->ctrl.ctrl->connector) {
+ SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n",
+ display);
+ return -ENOMEM;
+ }
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ connector = display->ctrl.ctrl->connector;
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "max_tmds_clock = %d\n",
+ connector->max_tmds_clock);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "latency_present %d %d\n",
+ connector->latency_present[0],
+ connector->latency_present[1]);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "video_latency %d %d\n",
+ connector->video_latency[0],
+ connector->video_latency[1]);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "audio_latency %d %d\n",
+ connector->audio_latency[0],
+ connector->audio_latency[1]);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "dvi_dual %d\n",
+ (int)connector->dvi_dual);
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ SDE_HDMI_DEBUG("%s - ", __func__);
+ return len;
+}
+
+static ssize_t _sde_hdmi_debugfs_edid_hdr_info_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[200];
+ u32 len = 0;
+ struct drm_connector *connector;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl ||
+ !display->ctrl.ctrl->connector) {
+ SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n",
+ display);
+ return -ENOMEM;
+ }
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ connector = display->ctrl.ctrl->connector;
+ len += snprintf(buf, sizeof(buf), "hdr_eotf = %d\n"
+ "hdr_metadata_type_one %d\n"
+ "hdr_max_luminance %d\n"
+ "hdr_avg_luminance %d\n"
+ "hdr_min_luminance %d\n"
+ "hdr_supported %d\n",
+ connector->hdr_eotf,
+ connector->hdr_metadata_type_one,
+ connector->hdr_max_luminance,
+ connector->hdr_avg_luminance,
+ connector->hdr_min_luminance,
+ (int)connector->hdr_supported);
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ SDE_HDMI_DEBUG("%s - ", __func__);
+ return len;
+}
+
+static ssize_t _sde_hdmi_debugfs_edid_hfvsdb_info_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[200];
+ u32 len = 0;
+ struct drm_connector *connector;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl ||
+ !display->ctrl.ctrl->connector) {
+ SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n",
+ display);
+ return -ENOMEM;
+ }
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ connector = display->ctrl.ctrl->connector;
+ len += snprintf(buf, PAGE_SIZE - len, "max_tmds_char = %d\n"
+ "scdc_present %d\n"
+ "rr_capable %d\n"
+ "supports_scramble %d\n"
+ "flags_3d %d\n",
+ connector->max_tmds_char,
+ (int)connector->scdc_present,
+ (int)connector->rr_capable,
+ (int)connector->supports_scramble,
+ connector->flags_3d);
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ return len;
+}
+
+static ssize_t _sde_hdmi_debugfs_edid_vcdb_info_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[100];
+ u32 len = 0;
+ struct drm_connector *connector;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl ||
+ !display->ctrl.ctrl->connector) {
+ SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n",
+ display);
+ return -ENOMEM;
+ }
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ connector = display->ctrl.ctrl->connector;
+ len += snprintf(buf, PAGE_SIZE - len, "pt_scan_info = %d\n"
+ "it_scan_info = %d\n"
+ "ce_scan_info = %d\n",
+ (int)connector->pt_scan_info,
+ (int)connector->it_scan_info,
+ (int)connector->ce_scan_info);
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ SDE_HDMI_DEBUG("%s - ", __func__);
+ return len;
+}
+
+static ssize_t _sde_hdmi_edid_vendor_name_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[100];
+ u32 len = 0;
+ struct drm_connector *connector;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl ||
+ !display->ctrl.ctrl->connector) {
+ SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n",
+ display);
+ return -ENOMEM;
+ }
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ connector = display->ctrl.ctrl->connector;
+ len += snprintf(buf, PAGE_SIZE - len, "Vendor ID is %s\n",
+ display->edid_ctrl->vendor_id);
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ SDE_HDMI_DEBUG("%s - ", __func__);
+ return len;
+}
static const struct file_operations dump_info_fops = {
.open = simple_open,
.read = _sde_hdmi_debugfs_dump_info_read,
};
+static const struct file_operations edid_modes_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_debugfs_edid_modes_read,
+};
+
+static const struct file_operations edid_vsdb_info_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_debugfs_edid_vsdb_info_read,
+};
+
+static const struct file_operations edid_hdr_info_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_debugfs_edid_hdr_info_read,
+};
+
+static const struct file_operations edid_hfvsdb_info_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_debugfs_edid_hfvsdb_info_read,
+};
+
+static const struct file_operations edid_vcdb_info_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_debugfs_edid_vcdb_info_read,
+};
+
+static const struct file_operations edid_vendor_name_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_edid_vendor_name_read,
+};
+
static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
{
int rc = 0;
- struct dentry *dir, *dump_file;
+ struct dentry *dir, *dump_file, *edid_modes;
+ struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info;
+ struct dentry *edid_vcdb_info, *edid_vendor_name;
dir = debugfs_create_dir(display->name, NULL);
if (!dir) {
@@ -100,6 +428,83 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
goto error_remove_dir;
}
+ edid_modes = debugfs_create_file("edid_modes",
+ 0444,
+ dir,
+ display,
+ &edid_modes_fops);
+
+ if (IS_ERR_OR_NULL(edid_modes)) {
+ rc = PTR_ERR(edid_modes);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ edid_vsdb_info = debugfs_create_file("edid_vsdb_info",
+ 0444,
+ dir,
+ display,
+ &edid_vsdb_info_fops);
+
+ if (IS_ERR_OR_NULL(edid_vsdb_info)) {
+ rc = PTR_ERR(edid_vsdb_info);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ edid_hdr_info = debugfs_create_file("edid_hdr_info",
+ 0444,
+ dir,
+ display,
+ &edid_hdr_info_fops);
+ if (IS_ERR_OR_NULL(edid_hdr_info)) {
+ rc = PTR_ERR(edid_hdr_info);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ edid_hfvsdb_info = debugfs_create_file("edid_hfvsdb_info",
+ 0444,
+ dir,
+ display,
+ &edid_hfvsdb_info_fops);
+
+ if (IS_ERR_OR_NULL(edid_hfvsdb_info)) {
+ rc = PTR_ERR(edid_hfvsdb_info);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ edid_vcdb_info = debugfs_create_file("edid_vcdb_info",
+ 0444,
+ dir,
+ display,
+ &edid_vcdb_info_fops);
+
+ if (IS_ERR_OR_NULL(edid_vcdb_info)) {
+ rc = PTR_ERR(edid_vcdb_info);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ edid_vendor_name = debugfs_create_file("edid_vendor_name",
+ 0444,
+ dir,
+ display,
+ &edid_vendor_name_fops);
+
+ if (IS_ERR_OR_NULL(edid_vendor_name)) {
+ rc = PTR_ERR(edid_vendor_name);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
display->root = dir;
return rc;
error_remove_dir:
@@ -395,20 +800,28 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work)
struct sde_hdmi *sde_hdmi =
container_of(work, struct sde_hdmi, hpd_work);
struct drm_connector *connector;
+ struct hdmi *hdmi = NULL;
+ u32 hdmi_ctrl;
if (!sde_hdmi || !sde_hdmi->ctrl.ctrl ||
- !sde_hdmi->ctrl.ctrl->connector) {
+ !sde_hdmi->ctrl.ctrl->connector ||
+ !sde_hdmi->edid_ctrl) {
SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n",
sde_hdmi);
return;
}
-
+ hdmi = sde_hdmi->ctrl.ctrl;
connector = sde_hdmi->ctrl.ctrl->connector;
- if (sde_hdmi->connected)
- sde_hdmi_get_edid(connector, sde_hdmi);
- else
- sde_hdmi_free_edid(sde_hdmi);
+ if (sde_hdmi->connected) {
+ hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
+ hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
+ sde_get_edid(connector, hdmi->i2c,
+ (void **)&sde_hdmi->edid_ctrl);
+ hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
+ hdmi->hdmi_mode = sde_detect_hdmi_monitor(sde_hdmi->edid_ctrl);
+ } else
+ sde_free_edid((void **)&sde_hdmi->edid_ctrl);
sde_hdmi_notify_clients(connector, sde_hdmi->connected);
drm_helper_hpd_irq_event(connector->dev);
@@ -431,7 +844,7 @@ static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi)
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
HDMI_HPD_INT_CTRL_INT_ACK);
- DRM_DEBUG("status=%04x, ctrl=%04x", hpd_int_status,
+ SDE_HDMI_DEBUG("status=%04x, ctrl=%04x", hpd_int_status,
hpd_int_ctrl);
/* detect disconnect if we are connected or visa versa: */
@@ -504,11 +917,11 @@ static int _sde_hdmi_get_audio_edid_blk(struct platform_device *pdev,
return -ENODEV;
}
- blk->audio_data_blk = display->edid.audio_data_block;
- blk->audio_data_blk_size = display->edid.adb_size;
+ blk->audio_data_blk = display->edid_ctrl->audio_data_block;
+ blk->audio_data_blk_size = display->edid_ctrl->adb_size;
- blk->spk_alloc_data_blk = display->edid.spkr_alloc_data_block;
- blk->spk_alloc_data_blk_size = display->edid.sadb_size;
+ blk->spk_alloc_data_blk = display->edid_ctrl->spkr_alloc_data_block;
+ blk->spk_alloc_data_blk_size = display->edid_ctrl->sadb_size;
return 0;
}
@@ -634,10 +1047,249 @@ void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
spin_unlock_irqrestore(&hdmi->reg_lock, flags);
- DRM_DEBUG("HDMI Core: %s, HDMI_CTRL=0x%08x\n",
+ SDE_HDMI_DEBUG("HDMI Core: %s, HDMI_CTRL=0x%08x\n",
power_on ? "Enable" : "Disable", ctrl);
}
+int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len)
+{
+ int rc;
+ int retry = 5;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = addr >> 1,
+ .flags = 0,
+ .len = 1,
+ .buf = &offset,
+ }, {
+ .addr = addr >> 1,
+ .flags = I2C_M_RD,
+ .len = data_len,
+ .buf = data,
+ }
+ };
+
+ SDE_HDMI_DEBUG("Start DDC read");
+ retry:
+ rc = i2c_transfer(hdmi->i2c, msgs, 2);
+
+ retry--;
+ if (rc == 2)
+ rc = 0;
+ else if (retry > 0)
+ goto retry;
+ else
+ rc = -EIO;
+
+ SDE_HDMI_DEBUG("End DDC read %d", rc);
+
+ return rc;
+}
+
+#define DDC_WRITE_MAX_BYTE_NUM 32
+
+int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len)
+{
+ int rc;
+ int retry = 10;
+ u8 buf[DDC_WRITE_MAX_BYTE_NUM];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = addr >> 1,
+ .flags = 0,
+ .len = 1,
+ }
+ };
+
+ SDE_HDMI_DEBUG("Start DDC write");
+ if (data_len > (DDC_WRITE_MAX_BYTE_NUM - 1)) {
+ SDE_ERROR("%s: write size too big\n", __func__);
+ return -ERANGE;
+ }
+
+ buf[0] = offset;
+ memcpy(&buf[1], data, data_len);
+ msgs[0].buf = buf;
+ msgs[0].len = data_len + 1;
+ retry:
+ rc = i2c_transfer(hdmi->i2c, msgs, 1);
+
+ retry--;
+ if (rc == 1)
+ rc = 0;
+ else if (retry > 0)
+ goto retry;
+ else
+ rc = -EIO;
+
+ SDE_HDMI_DEBUG("End DDC write %d", rc);
+
+ return rc;
+}
+
+int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val)
+{
+ int rc = 0;
+ u8 data_buf[2] = {0};
+ u16 dev_addr, data_len;
+ u8 offset;
+
+ if (!hdmi || !hdmi->i2c || !val) {
+ SDE_ERROR("Bad Parameters\n");
+ return -EINVAL;
+ }
+
+ if (data_type >= HDMI_TX_SCDC_MAX) {
+ SDE_ERROR("Unsupported data type\n");
+ return -EINVAL;
+ }
+
+ dev_addr = 0xA8;
+
+ switch (data_type) {
+ case HDMI_TX_SCDC_SCRAMBLING_STATUS:
+ data_len = 1;
+ offset = HDMI_SCDC_SCRAMBLER_STATUS;
+ break;
+ case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
+ case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
+ data_len = 1;
+ offset = HDMI_SCDC_TMDS_CONFIG;
+ break;
+ case HDMI_TX_SCDC_CLOCK_DET_STATUS:
+ case HDMI_TX_SCDC_CH0_LOCK_STATUS:
+ case HDMI_TX_SCDC_CH1_LOCK_STATUS:
+ case HDMI_TX_SCDC_CH2_LOCK_STATUS:
+ data_len = 1;
+ offset = HDMI_SCDC_STATUS_FLAGS_0;
+ break;
+ case HDMI_TX_SCDC_CH0_ERROR_COUNT:
+ data_len = 2;
+ offset = HDMI_SCDC_ERR_DET_0_L;
+ break;
+ case HDMI_TX_SCDC_CH1_ERROR_COUNT:
+ data_len = 2;
+ offset = HDMI_SCDC_ERR_DET_1_L;
+ break;
+ case HDMI_TX_SCDC_CH2_ERROR_COUNT:
+ data_len = 2;
+ offset = HDMI_SCDC_ERR_DET_2_L;
+ break;
+ case HDMI_TX_SCDC_READ_ENABLE:
+ data_len = 1;
+ offset = HDMI_SCDC_CONFIG_0;
+ break;
+ default:
+ break;
+ }
+
+ rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, data_buf, data_len);
+ if (rc) {
+ SDE_ERROR("DDC Read failed for %d\n", data_type);
+ return rc;
+ }
+
+ switch (data_type) {
+ case HDMI_TX_SCDC_SCRAMBLING_STATUS:
+ *val = (data_buf[0] & BIT(0)) ? 1 : 0;
+ break;
+ case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
+ *val = (data_buf[0] & BIT(0)) ? 1 : 0;
+ break;
+ case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
+ *val = (data_buf[0] & BIT(1)) ? 1 : 0;
+ break;
+ case HDMI_TX_SCDC_CLOCK_DET_STATUS:
+ *val = (data_buf[0] & BIT(0)) ? 1 : 0;
+ break;
+ case HDMI_TX_SCDC_CH0_LOCK_STATUS:
+ *val = (data_buf[0] & BIT(1)) ? 1 : 0;
+ break;
+ case HDMI_TX_SCDC_CH1_LOCK_STATUS:
+ *val = (data_buf[0] & BIT(2)) ? 1 : 0;
+ break;
+ case HDMI_TX_SCDC_CH2_LOCK_STATUS:
+ *val = (data_buf[0] & BIT(3)) ? 1 : 0;
+ break;
+ case HDMI_TX_SCDC_CH0_ERROR_COUNT:
+ case HDMI_TX_SCDC_CH1_ERROR_COUNT:
+ case HDMI_TX_SCDC_CH2_ERROR_COUNT:
+ if (data_buf[1] & BIT(7))
+ *val = (data_buf[0] | ((data_buf[1] & 0x7F) << 8));
+ else
+ *val = 0;
+ break;
+ case HDMI_TX_SCDC_READ_ENABLE:
+ *val = (data_buf[0] & BIT(0)) ? 1 : 0;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val)
+{
+ int rc = 0;
+ u8 data_buf[2] = {0};
+ u8 read_val = 0;
+ u16 dev_addr, data_len;
+ u8 offset;
+
+ if (!hdmi || !hdmi->i2c) {
+ SDE_ERROR("Bad Parameters\n");
+ return -EINVAL;
+ }
+
+ if (data_type >= HDMI_TX_SCDC_MAX) {
+ SDE_ERROR("Unsupported data type\n");
+ return -EINVAL;
+ }
+
+ dev_addr = 0xA8;
+
+ switch (data_type) {
+ case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
+ case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
+ dev_addr = 0xA8;
+ data_len = 1;
+ offset = HDMI_SCDC_TMDS_CONFIG;
+ rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, &read_val,
+ data_len);
+ if (rc) {
+ SDE_ERROR("scdc read failed\n");
+ return rc;
+ }
+ if (data_type == HDMI_TX_SCDC_SCRAMBLING_ENABLE) {
+ data_buf[0] = ((((u8)(read_val & 0xFF)) & (~BIT(0))) |
+ ((u8)(val & BIT(0))));
+ } else {
+ data_buf[0] = ((((u8)(read_val & 0xFF)) & (~BIT(1))) |
+ (((u8)(val & BIT(0))) << 1));
+ }
+ break;
+ case HDMI_TX_SCDC_READ_ENABLE:
+ data_len = 1;
+ offset = HDMI_SCDC_CONFIG_0;
+ data_buf[0] = (u8)(val & 0x1);
+ break;
+ default:
+ SDE_ERROR("Cannot write to read only reg (%d)\n",
+ data_type);
+ return -EINVAL;
+ }
+
+ rc = sde_hdmi_ddc_write(hdmi, dev_addr, offset, data_buf, data_len);
+ if (rc) {
+ SDE_ERROR("DDC Read failed for %d\n", data_type);
+ return rc;
+ }
+ return 0;
+}
+
int sde_hdmi_get_info(struct msm_display_info *info,
void *display)
{
@@ -746,7 +1398,7 @@ int sde_hdmi_connector_post_init(struct drm_connector *connector,
if (info)
sde_kms_info_add_keystr(info,
- "DISPLAY_TYPE",
+ "display type",
sde_hdmi->display_type);
hdmi->connector = connector;
@@ -775,8 +1427,6 @@ sde_hdmi_connector_detect(struct drm_connector *connector,
return status;
}
- SDE_DEBUG("\n");
-
/* get display dsi_info */
memset(&info, 0x0, sizeof(info));
rc = sde_hdmi_get_info(&info, display);
@@ -797,25 +1447,6 @@ sde_hdmi_connector_detect(struct drm_connector *connector,
return status;
}
-int _sde_hdmi_update_modes(struct drm_connector *connector,
- struct sde_hdmi *display)
-{
- int rc = 0;
- struct hdmi_edid_ctrl *edid_ctrl = &display->edid;
-
- if (edid_ctrl->edid) {
- drm_mode_connector_update_edid_property(connector,
- edid_ctrl->edid);
-
- rc = drm_add_edid_modes(connector, edid_ctrl->edid);
- return rc;
- }
-
- drm_mode_connector_update_edid_property(connector, NULL);
-
- return rc;
-}
-
int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
{
struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
@@ -828,8 +1459,6 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
return 0;
}
- SDE_DEBUG("\n");
-
if (hdmi_display->non_pluggable) {
list_for_each_entry(mode, &hdmi_display->mode_list, head) {
m = drm_mode_duplicate(connector->dev, mode);
@@ -842,7 +1471,9 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
}
ret = hdmi_display->num_of_modes;
} else {
- ret = _sde_hdmi_update_modes(connector, display);
+ /* pluggable case assumes EDID is read when HPD */
+ ret = _sde_edid_update_modes(connector,
+ hdmi_display->edid_ctrl);
}
return ret;
@@ -864,8 +1495,6 @@ enum drm_mode_status sde_hdmi_mode_valid(struct drm_connector *connector,
return 0;
}
- SDE_DEBUG("\n");
-
hdmi = hdmi_display->ctrl.ctrl;
priv = connector->dev->dev_private;
kms = priv->kms;
@@ -873,7 +1502,7 @@ enum drm_mode_status sde_hdmi_mode_valid(struct drm_connector *connector,
actual = kms->funcs->round_pixclk(kms,
requested, hdmi->encoder);
- SDE_DEBUG("requested=%ld, actual=%ld", requested, actual);
+ SDE_HDMI_DEBUG("requested=%ld, actual=%ld", requested, actual);
if (actual != requested)
return MODE_CLOCK_RANGE;
@@ -909,7 +1538,7 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
struct msm_drm_private *priv = NULL;
struct platform_device *pdev = to_platform_device(dev);
- SDE_ERROR("E\n");
+ SDE_HDMI_DEBUG(" %s +\n", __func__);
if (!dev || !pdev || !master) {
pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n",
dev, pdev, master);
@@ -941,18 +1570,20 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
goto error;
}
- rc = sde_hdmi_edid_init(display);
- if (rc) {
- SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n",
- display->name, rc);
+ display->edid_ctrl = sde_edid_init();
+ if (!display->edid_ctrl) {
+ SDE_ERROR("[%s]sde edid init failed\n",
+ display->name);
+ rc = -ENOMEM;
goto error;
}
display_ctrl = &display->ctrl;
display_ctrl->ctrl = priv->hdmi;
- SDE_ERROR("display_ctrl->ctrl=%p\n", display_ctrl->ctrl);
display->drm_dev = drm;
+ mutex_unlock(&display->display_lock);
+ return rc;
error:
(void)_sde_hdmi_debugfs_deinit(display);
debug_error:
@@ -978,7 +1609,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master,
}
mutex_lock(&display->display_lock);
(void)_sde_hdmi_debugfs_deinit(display);
- (void)sde_hdmi_edid_deinit(display);
+ (void)sde_edid_deinit((void **)&display->edid_ctrl);
display->drm_dev = NULL;
mutex_unlock(&display->display_lock);
}
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
index 869d1bebf9db..bb3061a6ed00 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -25,9 +25,13 @@
#include <drm/drm_crtc.h>
#include "hdmi.h"
-#define MAX_NUMBER_ADB 5
-#define MAX_AUDIO_DATA_BLOCK_SIZE 30
-#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3
+#include "sde_edid_parser.h"
+
+#ifdef HDMI_DEBUG_ENABLE
+#define SDE_HDMI_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
+#else
+#define SDE_HDMI_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
+#endif
/**
* struct sde_hdmi_info - defines hdmi display properties
@@ -64,14 +68,6 @@ struct sde_hdmi_ctrl {
u32 hdmi_ctrl_idx;
};
-struct hdmi_edid_ctrl {
- struct edid *edid;
- u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE];
- int adb_size;
- u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
- int sadb_size;
-};
-
/**
* struct sde_hdmi - hdmi display information
* @pdev: Pointer to platform device.
@@ -102,7 +98,7 @@ struct sde_hdmi {
struct platform_device *ext_pdev;
struct msm_ext_disp_init_data ext_audio_data;
- struct hdmi_edid_ctrl edid;
+ struct sde_edid_ctrl *edid_ctrl;
bool non_pluggable;
u32 num_of_modes;
@@ -116,6 +112,38 @@ struct sde_hdmi {
struct dentry *root;
};
+/**
+ * hdmi_tx_scdc_access_type() - hdmi 2.0 DDC functionalities.
+ */
+enum hdmi_tx_scdc_access_type {
+ HDMI_TX_SCDC_SCRAMBLING_STATUS,
+ HDMI_TX_SCDC_SCRAMBLING_ENABLE,
+ HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE,
+ HDMI_TX_SCDC_CLOCK_DET_STATUS,
+ HDMI_TX_SCDC_CH0_LOCK_STATUS,
+ HDMI_TX_SCDC_CH1_LOCK_STATUS,
+ HDMI_TX_SCDC_CH2_LOCK_STATUS,
+ HDMI_TX_SCDC_CH0_ERROR_COUNT,
+ HDMI_TX_SCDC_CH1_ERROR_COUNT,
+ HDMI_TX_SCDC_CH2_ERROR_COUNT,
+ HDMI_TX_SCDC_READ_ENABLE,
+ HDMI_TX_SCDC_MAX,
+};
+
+#define HDMI_KHZ_TO_HZ 1000
+#define HDMI_MHZ_TO_HZ 1000000
+/**
+ * hdmi_tx_ddc_timer_type() - hdmi DDC timer functionalities.
+ */
+enum hdmi_tx_ddc_timer_type {
+ HDMI_TX_DDC_TIMER_HDCP2P2_RD_MSG,
+ HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS,
+ HDMI_TX_DDC_TIMER_UPDATE_FLAGS,
+ HDMI_TX_DDC_TIMER_STATUS_FLAGS,
+ HDMI_TX_DDC_TIMER_CED,
+ HDMI_TX_DDC_TIMER_MAX,
+ };
+
#ifdef CONFIG_DRM_SDE_HDMI
/**
* sde_hdmi_get_num_of_displays() - returns number of display devices
@@ -259,6 +287,52 @@ struct drm_bridge *sde_hdmi_bridge_init(struct hdmi *hdmi);
void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on);
/**
+ * sde_hdmi_ddc_read() - common hdmi ddc read API.
+ * @hdmi: Handle to the hdmi.
+ * @addr: Command address.
+ * @offset: Command offset.
+ * @data: Data buffer for read back.
+ * @data_len: Data buffer length.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len);
+
+/**
+ * sde_hdmi_ddc_write() - common hdmi ddc write API.
+ * @hdmi: Handle to the hdmi.
+ * @addr: Command address.
+ * @offset: Command offset.
+ * @data: Data buffer for write.
+ * @data_len: Data buffer length.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len);
+
+/**
+ * sde_hdmi_scdc_read() - hdmi 2.0 ddc read API.
+ * @hdmi: Handle to the hdmi.
+ * @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type.
+ * @val: Read back value.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val);
+
+/**
+ * sde_hdmi_scdc_write() - hdmi 2.0 ddc write API.
+ * @hdmi: Handle to the hdmi.
+ * @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type.
+ * @val: Value write through DDC.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val);
+
+/**
* sde_hdmi_audio_on() - enable hdmi audio.
* @hdmi: Handle to the hdmi.
* @params: audio setup parameters from codec.
@@ -305,40 +379,6 @@ void sde_hdmi_notify_clients(struct drm_connector *connector,
void sde_hdmi_ack_state(struct drm_connector *connector,
enum drm_connector_status status);
-/**
- * sde_hdmi_edid_init() - init edid structure.
- * @display: Handle to the sde_hdmi.
- *
- * Return: error code.
- */
-int sde_hdmi_edid_init(struct sde_hdmi *display);
-
-/**
- * sde_hdmi_edid_deinit() - deinit edid structure.
- * @display: Handle to the sde_hdmi.
- *
- * Return: error code.
- */
-int sde_hdmi_edid_deinit(struct sde_hdmi *display);
-
-/**
- * sde_hdmi_get_edid() - get edid info.
- * @connector: Handle to the drm_connector.
- * @display: Handle to the sde_hdmi.
- *
- * Return: void.
- */
-void sde_hdmi_get_edid(struct drm_connector *connector,
- struct sde_hdmi *display);
-
-/**
- * sde_hdmi_free_edid() - free edid structure.
- * @display: Handle to the sde_hdmi.
- *
- * Return: error code.
- */
-int sde_hdmi_free_edid(struct sde_hdmi *display);
-
#else /*#ifdef CONFIG_DRM_SDE_HDMI*/
static inline u32 sde_hdmi_get_num_of_displays(void)
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
index 681dca501f9b..c76e42c67885 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
@@ -28,6 +28,13 @@ struct sde_hdmi_bridge {
};
#define to_hdmi_bridge(x) container_of(x, struct sde_hdmi_bridge, base)
+/* TX major version that supports scrambling */
+#define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04
+#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000
+#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200
+/* default hsyncs for 4k@60 for 200ms */
+#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571
+
/* for AVI program */
#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \
(HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE)
@@ -101,6 +108,265 @@ static void _sde_hdmi_bridge_power_off(struct drm_bridge *bridge)
}
}
+static int _sde_hdmi_bridge_ddc_clear_irq(struct hdmi *hdmi,
+ char *what)
+{
+ u32 ddc_int_ctrl, ddc_status, in_use, timeout;
+ u32 sw_done_mask = BIT(2);
+ u32 sw_done_ack = BIT(1);
+ u32 in_use_by_sw = BIT(0);
+ u32 in_use_by_hw = BIT(1);
+
+ /* clear and enable interrutps */
+ ddc_int_ctrl = sw_done_mask | sw_done_ack;
+
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL, ddc_int_ctrl);
+
+ /* wait until DDC HW is free */
+ timeout = 100;
+ do {
+ ddc_status = hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS);
+ in_use = ddc_status & (in_use_by_sw | in_use_by_hw);
+ if (in_use) {
+ SDE_DEBUG("ddc is in use by %s, timeout(%d)\n",
+ ddc_status & in_use_by_sw ? "sw" : "hw",
+ timeout);
+ udelay(100);
+ }
+ } while (in_use && --timeout);
+
+ if (!timeout) {
+ SDE_ERROR("%s: timedout\n", what);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int _sde_hdmi_bridge_scrambler_ddc_check_status(struct hdmi *hdmi)
+{
+ int rc = 0;
+ u32 reg_val;
+
+ /* check for errors and clear status */
+ reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS);
+ if (reg_val & BIT(4)) {
+ SDE_ERROR("ddc aborted\n");
+ reg_val |= BIT(5);
+ rc = -ECONNABORTED;
+ }
+
+ if (reg_val & BIT(8)) {
+ SDE_ERROR("timed out\n");
+ reg_val |= BIT(9);
+ rc = -ETIMEDOUT;
+ }
+
+ if (reg_val & BIT(12)) {
+ SDE_ERROR("NACK0\n");
+ reg_val |= BIT(13);
+ rc = -EIO;
+ }
+ if (reg_val & BIT(14)) {
+ SDE_ERROR("NACK1\n");
+ reg_val |= BIT(15);
+ rc = -EIO;
+ }
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS, reg_val);
+
+ return rc;
+}
+
+static void _sde_hdmi_bridge_scrambler_ddc_reset(struct hdmi *hdmi)
+{
+ u32 reg_val;
+
+ /* clear ack and disable interrupts */
+ reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1);
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val);
+
+ /* Reset DDC timers */
+ reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
+
+ reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
+ reg_val &= ~BIT(0);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
+}
+
+static void _sde_hdmi_bridge_scrambler_ddc_disable(struct hdmi *hdmi)
+{
+ u32 reg_val;
+
+ _sde_hdmi_bridge_scrambler_ddc_reset(hdmi);
+ /* Disable HW DDC access to RxStatus register */
+ reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(8) | BIT(9));
+ hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val);
+}
+
+static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi,
+ u32 timeout_hsync)
+{
+ u32 reg_val;
+ int rc;
+
+ _sde_hdmi_bridge_ddc_clear_irq(hdmi, "scrambler");
+
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL,
+ timeout_hsync);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2,
+ timeout_hsync);
+ reg_val = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL5);
+ reg_val |= BIT(10);
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL5, reg_val);
+
+ reg_val = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL2);
+ /* Trigger interrupt if scrambler status is 0 or DDC failure */
+ reg_val |= BIT(10);
+ reg_val &= ~(BIT(15) | BIT(16));
+ reg_val |= BIT(16);
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val);
+
+ /* Enable DDC access */
+ reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL);
+
+ reg_val &= ~(BIT(8) | BIT(9));
+ reg_val |= BIT(8);
+ hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val);
+
+ /* WAIT for 200ms as per HDMI 2.0 standard for sink to respond */
+ msleep(200);
+
+ /* clear the scrambler status */
+ rc = _sde_hdmi_bridge_scrambler_ddc_check_status(hdmi);
+ if (rc)
+ SDE_ERROR("scrambling ddc error %d\n", rc);
+
+ _sde_hdmi_bridge_scrambler_ddc_disable(hdmi);
+
+ return rc;
+}
+
+static int _sde_hdmi_bridge_setup_ddc_timers(struct hdmi *hdmi,
+ u32 type, u32 to_in_num_lines)
+{
+ if (type >= HDMI_TX_DDC_TIMER_MAX) {
+ SDE_ERROR("Invalid timer type %d\n", type);
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS:
+ _sde_hdmi_bridge_scrambler_status_timer_setup(hdmi,
+ to_in_num_lines);
+ break;
+ default:
+ SDE_ERROR("%d type not supported\n", type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int _sde_hdmi_bridge_get_timeout_in_hysnc(
+ struct drm_display_mode *mode, u32 timeout_ms)
+{
+ /*
+ * pixel clock = h_total * v_total * fps
+ * 1 sec = pixel clock number of pixels are transmitted.
+ * time taken by one line (h_total) = 1s / (v_total * fps).
+ * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps))
+ * = (time_ms * clock) / h_total
+ */
+
+ return (timeout_ms * mode->clock / mode->htotal);
+}
+
+static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ int rc = 0;
+ int timeout_hsync;
+ u32 reg_val = 0;
+ u32 tmds_clock_ratio = 0;
+ bool scrambler_on = false;
+
+ struct drm_connector *connector = NULL;
+
+ if (!hdmi || !mode) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+ connector = hdmi->connector;
+
+ /* Read HDMI version */
+ reg_val = hdmi_read(hdmi, REG_HDMI_VERSION);
+ reg_val = (reg_val & 0xF0000000) >> 28;
+ /* Scrambling is supported from HDMI TX 4.0 */
+ if (reg_val < HDMI_TX_SCRAMBLER_MIN_TX_VERSION) {
+ DRM_INFO("scrambling not supported by tx\n");
+ return 0;
+ }
+
+ if (mode->clock > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
+ scrambler_on = true;
+ tmds_clock_ratio = 1;
+ } else {
+ scrambler_on = connector->supports_scramble;
+ }
+
+ DRM_INFO("scrambler %s\n", scrambler_on ? "on" : "off");
+
+ if (scrambler_on) {
+ rc = sde_hdmi_scdc_write(hdmi,
+ HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE,
+ tmds_clock_ratio);
+ if (rc) {
+ SDE_ERROR("TMDS CLK RATIO ERR\n");
+ return rc;
+ }
+
+ reg_val = hdmi_read(hdmi, REG_HDMI_CTRL);
+ reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */
+ reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */
+
+ hdmi_write(hdmi, REG_HDMI_CTRL, reg_val);
+
+ rc = sde_hdmi_scdc_write(hdmi,
+ HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x1);
+ if (rc) {
+ SDE_ERROR("failed to enable scrambling\n");
+ return rc;
+ }
+
+ /*
+ * Setup hardware to periodically check for scrambler
+ * status bit on the sink. Sink should set this bit
+ * with in 200ms after scrambler is enabled.
+ */
+ timeout_hsync = _sde_hdmi_bridge_get_timeout_in_hysnc(
+ mode,
+ HDMI_TX_SCRAMBLER_TIMEOUT_MSEC);
+ if (timeout_hsync <= 0) {
+ SDE_ERROR("err in timeout hsync calc\n");
+ timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
+ }
+ SDE_DEBUG("timeout for scrambling en: %d hsyncs\n",
+ timeout_hsync);
+
+ rc = _sde_hdmi_bridge_setup_ddc_timers(hdmi,
+ HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, timeout_hsync);
+ } else {
+ sde_hdmi_scdc_write(hdmi, HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0);
+ reg_val = hdmi_read(hdmi, REG_HDMI_CTRL);
+ reg_val &= ~BIT(31); /* Disable Update DATAPATH_MODE */
+ reg_val &= ~BIT(28); /* Unset SCRAMBLER_EN bit */
+ hdmi_write(hdmi, REG_HDMI_CTRL, reg_val);
+ }
+ return rc;
+}
+
static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
{
struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
@@ -373,6 +639,7 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
_sde_hdmi_bridge_set_spd_infoframe(hdmi, mode);
DRM_DEBUG("hdmi setup info frame\n");
}
+ _sde_hdmi_bridge_setup_scrambler(hdmi, mode);
}
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c
deleted file mode 100644
index 57c79e2aa812..000000000000
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <drm/drm_edid.h>
-
-#include "sde_kms.h"
-#include "sde_hdmi.h"
-
-/* TODO: copy from drm_edid.c and mdss_hdmi_edid.c. remove if using ELD */
-#define DBC_START_OFFSET 4
-#define EDID_DTD_LEN 18
-
-enum data_block_types {
- RESERVED,
- AUDIO_DATA_BLOCK,
- VIDEO_DATA_BLOCK,
- VENDOR_SPECIFIC_DATA_BLOCK,
- SPEAKER_ALLOCATION_DATA_BLOCK,
- VESA_DTC_DATA_BLOCK,
- RESERVED2,
- USE_EXTENDED_TAG
-};
-
-static u8 *_sde_hdmi_edid_find_cea_extension(struct edid *edid)
-{
- u8 *edid_ext = NULL;
- int i;
-
- /* No EDID or EDID extensions */
- if (edid == NULL || edid->extensions == 0)
- return NULL;
-
- /* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == CEA_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return NULL;
-
- return edid_ext;
-}
-
-static const u8 *_sde_hdmi_edid_find_block(const u8 *in_buf, u32 start_offset,
- u8 type, u8 *len)
-{
- /* the start of data block collection, start of Video Data Block */
- u32 offset = start_offset;
- u32 dbc_offset = in_buf[2];
-
- /*
- * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block
- * collection present.
- * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block
- * collection present and no DTD data present.
- */
- if ((dbc_offset == 0) || (dbc_offset == 4)) {
- SDE_ERROR("EDID: no DTD or non-DTD data present\n");
- return NULL;
- }
-
- while (offset < dbc_offset) {
- u8 block_len = in_buf[offset] & 0x1F;
-
- if ((offset + block_len <= dbc_offset) &&
- (in_buf[offset] >> 5) == type) {
- *len = block_len;
- SDE_DEBUG("EDID: block=%d found @ 0x%x w/ len=%d\n",
- type, offset, block_len);
-
- return in_buf + offset;
- }
- offset += 1 + block_len;
- }
-
- return NULL;
-}
-
-static void _sde_hdmi_extract_audio_data_blocks(
- struct hdmi_edid_ctrl *edid_ctrl)
-{
- u8 len = 0;
- u8 adb_max = 0;
- const u8 *adb = NULL;
- u32 offset = DBC_START_OFFSET;
- u8 *cea = NULL;
-
- if (!edid_ctrl) {
- SDE_ERROR("invalid edid_ctrl\n");
- return;
- }
-
- cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid);
- if (!cea) {
- SDE_DEBUG("CEA extension not found\n");
- return;
- }
-
- edid_ctrl->adb_size = 0;
-
- memset(edid_ctrl->audio_data_block, 0,
- sizeof(edid_ctrl->audio_data_block));
-
- do {
- len = 0;
- adb = _sde_hdmi_edid_find_block(cea, offset, AUDIO_DATA_BLOCK,
- &len);
-
- if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE ||
- adb_max >= MAX_NUMBER_ADB)) {
- if (!edid_ctrl->adb_size) {
- SDE_DEBUG("No/Invalid Audio Data Block\n");
- return;
- }
-
- continue;
- }
-
- memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size,
- adb + 1, len);
- offset = (adb - cea) + 1 + len;
-
- edid_ctrl->adb_size += len;
- adb_max++;
- } while (adb);
-
-}
-
-static void _sde_hdmi_extract_speaker_allocation_data(
- struct hdmi_edid_ctrl *edid_ctrl)
-{
- u8 len;
- const u8 *sadb = NULL;
- u8 *cea = NULL;
-
- if (!edid_ctrl) {
- SDE_ERROR("invalid edid_ctrl\n");
- return;
- }
-
- cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid);
- if (!cea) {
- SDE_DEBUG("CEA extension not found\n");
- return;
- }
-
- sadb = _sde_hdmi_edid_find_block(cea, DBC_START_OFFSET,
- SPEAKER_ALLOCATION_DATA_BLOCK, &len);
- if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) {
- SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n");
- return;
- }
-
- memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len);
- edid_ctrl->sadb_size = len;
-
- SDE_DEBUG("EDID: speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n",
- sadb[1],
- (sadb[1] & BIT(0)) ? "FL/FR," : "",
- (sadb[1] & BIT(1)) ? "LFE," : "",
- (sadb[1] & BIT(2)) ? "FC," : "",
- (sadb[1] & BIT(3)) ? "RL/RR," : "",
- (sadb[1] & BIT(4)) ? "RC," : "",
- (sadb[1] & BIT(5)) ? "FLC/FRC," : "",
- (sadb[1] & BIT(6)) ? "RLC/RRC," : "");
-}
-
-int sde_hdmi_edid_init(struct sde_hdmi *display)
-{
- int rc = 0;
-
- if (!display) {
- SDE_ERROR("[%s]Invalid params\n", display->name);
- return -EINVAL;
- }
-
- memset(&display->edid, 0, sizeof(display->edid));
-
- return rc;
-}
-
-int sde_hdmi_free_edid(struct sde_hdmi *display)
-{
- struct hdmi_edid_ctrl *edid_ctrl = &display->edid;
-
- kfree(edid_ctrl->edid);
- edid_ctrl->edid = NULL;
-
- return 0;
-}
-
-int sde_hdmi_edid_deinit(struct sde_hdmi *display)
-{
- return sde_hdmi_free_edid(display);
-}
-
-void sde_hdmi_get_edid(struct drm_connector *connector,
- struct sde_hdmi *display)
-{
- u32 hdmi_ctrl;
- struct hdmi_edid_ctrl *edid_ctrl = &display->edid;
- struct hdmi *hdmi = display->ctrl.ctrl;
-
- /* Read EDID */
- hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
- hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
- edid_ctrl->edid = drm_get_edid(connector, hdmi->i2c);
- hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
-
- if (edid_ctrl->edid) {
- hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid_ctrl->edid);
-
- _sde_hdmi_extract_audio_data_blocks(edid_ctrl);
- _sde_hdmi_extract_speaker_allocation_data(edid_ctrl);
- }
-};
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
index aef20f76bf02..0956617442af 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
@@ -563,6 +563,20 @@ static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val)
#define REG_HDMI_CEC_WR_CHECK_CONFIG 0x00000370
+#define REG_HDMI_DDC_INT_CTRL0 0x00000430
+#define REG_HDMI_DDC_INT_CTRL1 0x00000434
+#define REG_HDMI_DDC_INT_CTRL2 0x00000438
+#define REG_HDMI_DDC_INT_CTRL3 0x0000043C
+#define REG_HDMI_DDC_INT_CTRL4 0x00000440
+#define REG_HDMI_DDC_INT_CTRL5 0x00000444
+#define REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL 0x00000464
+#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL 0x00000468
+#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2 0x0000046C
+#define REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS 0x00000470
+#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS 0x00000474
+#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS2 0x00000478
+#define REG_HDMI_HW_DDC_CTRL 0x000004CC
+
#define REG_HDMI_8x60_PHY_REG0 0x00000300
#define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK 0x0000001c
#define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__SHIFT 2
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index ac46c473791f..2045dc34c20a 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -125,6 +125,8 @@ struct msm_gem_submit {
uint32_t fence;
int ring;
bool valid;
+ uint64_t profile_buf_iova;
+ void *profile_buf_vaddr;
unsigned int nr_cmds;
unsigned int nr_bos;
struct {
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 0566cefaae81..52fc81420690 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -48,6 +48,9 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev,
submit->nr_bos = 0;
submit->nr_cmds = 0;
+ submit->profile_buf_vaddr = NULL;
+ submit->profile_buf_iova = 0;
+
INIT_LIST_HEAD(&submit->bo_list);
ww_acquire_init(&submit->ticket, &reservation_ww_class);
}
@@ -393,6 +396,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
case MSM_SUBMIT_CMD_BUF:
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
+ case MSM_SUBMIT_CMD_PROFILE_BUF:
break;
default:
DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
@@ -425,6 +429,12 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
submit->cmd[i].iova = iova + submit_cmd.submit_offset;
submit->cmd[i].idx = submit_cmd.submit_idx;
+ if (submit_cmd.type == MSM_SUBMIT_CMD_PROFILE_BUF) {
+ submit->profile_buf_iova = submit->cmd[i].iova;
+ submit->profile_buf_vaddr =
+ msm_gem_vaddr_locked(&msm_obj->base);
+ }
+
if (submit->valid)
continue;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 82da4c1bc86c..41322045ced3 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -51,6 +51,9 @@
#define LEFT_MIXER 0
#define RIGHT_MIXER 1
+/* indicates pending page flip events */
+#define PENDING_FLIP 0x2
+
static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
{
struct msm_drm_private *priv = crtc->dev->dev_private;
@@ -399,13 +402,18 @@ static void sde_crtc_vblank_cb(void *data)
{
struct drm_crtc *crtc = (struct drm_crtc *)data;
struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+ unsigned pending;
+ pending = atomic_xchg(&sde_crtc->pending, 0);
/* keep statistics on vblank callback - with auto reset via debugfs */
if (ktime_equal(sde_crtc->vblank_cb_time, ktime_set(0, 0)))
sde_crtc->vblank_cb_time = ktime_get();
else
sde_crtc->vblank_cb_count++;
- _sde_crtc_complete_flip(crtc, NULL);
+
+ if (pending & PENDING_FLIP)
+ _sde_crtc_complete_flip(crtc, NULL);
+
drm_crtc_handle_vblank(crtc);
DRM_DEBUG_VBL("crtc%d\n", crtc->base.id);
SDE_EVT32_IRQ(DRMID(crtc));
@@ -519,6 +527,28 @@ static void sde_crtc_frame_event_cb(void *data, u32 event)
queue_kthread_work(&priv->disp_thread[pipe_id].worker, &fevent->work);
}
+/**
+ * sde_crtc_request_flip_cb - callback to request page_flip events
+ * Once the HW flush is complete , userspace must be notified of
+ * PAGE_FLIP completed event in the next vblank event.
+ * Using this callback, a hint is set to signal any callers waiting
+ * for a PAGE_FLIP complete event.
+ * This is called within the enc_spinlock.
+ * @data: Pointer to cb private data
+ */
+static void sde_crtc_request_flip_cb(void *data)
+{
+ struct drm_crtc *crtc = (struct drm_crtc *)data;
+ struct sde_crtc *sde_crtc;
+
+ if (!crtc) {
+ SDE_ERROR("invalid parameters\n");
+ return;
+ }
+ sde_crtc = to_sde_crtc(crtc);
+ atomic_or(PENDING_FLIP, &sde_crtc->pending);
+}
+
void sde_crtc_complete_commit(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
@@ -679,7 +709,6 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
{
struct sde_crtc *sde_crtc;
struct drm_device *dev;
- unsigned long flags;
u32 i;
if (!crtc) {
@@ -701,14 +730,6 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
if (!sde_crtc->num_mixers)
_sde_crtc_setup_mixers(crtc);
- if (sde_crtc->event) {
- WARN_ON(sde_crtc->event);
- } else {
- spin_lock_irqsave(&dev->event_lock, flags);
- sde_crtc->event = crtc->state->event;
- spin_unlock_irqrestore(&dev->event_lock, flags);
- }
-
/* Reset flush mask from previous commit */
for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) {
struct sde_hw_ctl *ctl = sde_crtc->mixers[i].hw_ctl;
@@ -763,7 +784,8 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
dev = crtc->dev;
if (sde_crtc->event) {
- SDE_DEBUG("already received sde_crtc->event\n");
+ SDE_ERROR("%s already received sde_crtc->event\n",
+ sde_crtc->name);
} else {
spin_lock_irqsave(&dev->event_lock, flags);
sde_crtc->event = crtc->state->event;
@@ -999,6 +1021,7 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
if (encoder->crtc != crtc)
continue;
sde_encoder_register_frame_event_callback(encoder, NULL, NULL);
+ sde_encoder_register_request_flip_callback(encoder, NULL, NULL);
}
memset(sde_crtc->mixers, 0, sizeof(sde_crtc->mixers));
@@ -1039,6 +1062,8 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
continue;
sde_encoder_register_frame_event_callback(encoder,
sde_crtc_frame_event_cb, (void *)crtc);
+ sde_encoder_register_request_flip_callback(encoder,
+ sde_crtc_request_flip_cb, (void *)crtc);
}
for (i = 0; i < sde_crtc->num_mixers; i++) {
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 25a93e882e6d..97a20b987ef5 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -89,6 +89,7 @@ struct sde_crtc_frame_event {
* @frame_pending : Whether or not an update is pending
* @frame_events : static allocation of in-flight frame events
* @frame_event_list : available frame event list
+ * @pending : Whether any page-flip events are pending signal
* @spin_lock : spin lock for frame event, transaction status, etc...
*/
struct sde_crtc {
@@ -109,7 +110,7 @@ struct sde_crtc {
/* output fence support */
struct sde_fence output_fence;
-
+ atomic_t pending;
struct sde_hw_stage_cfg stage_cfg;
struct dentry *debugfs_root;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 8cffb03fdfbb..030b192e5df4 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -83,6 +83,8 @@
* Bit0 = phys_encs[0] etc.
* @crtc_frame_event_cb: callback handler for frame event
* @crtc_frame_event_cb_data: callback handler private data
+ * @crtc_request_flip_cb: callback handler for requesting page-flip event
+ * @crtc_request_flip_cb_data: callback handler private data
* @crtc_frame_event: callback event
* @frame_done_timeout: frame done timeout in Hz
* @frame_done_timer: watchdog timer for frame done event
@@ -107,8 +109,9 @@ struct sde_encoder_virt {
DECLARE_BITMAP(frame_busy_mask, MAX_PHYS_ENCODERS_PER_VIRTUAL);
void (*crtc_frame_event_cb)(void *, u32 event);
void *crtc_frame_event_cb_data;
+ void (*crtc_request_flip_cb)(void *);
+ void *crtc_request_flip_cb_data;
u32 crtc_frame_event;
-
atomic_t frame_done_timeout;
struct timer_list frame_done_timer;
};
@@ -584,6 +587,24 @@ void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc,
spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
}
+void sde_encoder_register_request_flip_callback(struct drm_encoder *drm_enc,
+ void (*request_flip_cb)(void *),
+ void *request_flip_cb_data)
+{
+ struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+ unsigned long lock_flags;
+
+ if (!drm_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+
+ spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
+ sde_enc->crtc_request_flip_cb = request_flip_cb;
+ sde_enc->crtc_request_flip_cb_data = request_flip_cb_data;
+ spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
+}
+
static void sde_encoder_frame_done_callback(
struct drm_encoder *drm_enc,
struct sde_encoder_phys *ready_phys, u32 event)
@@ -759,6 +780,11 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
pending_flush);
}
+ /* HW flush has happened, request a flip complete event now */
+ if (sde_enc->crtc_request_flip_cb)
+ sde_enc->crtc_request_flip_cb(
+ sde_enc->crtc_request_flip_cb_data);
+
_sde_encoder_trigger_start(sde_enc->cur_master);
spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 82576b479bf8..6b74dca13ae9 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -72,6 +72,17 @@ void sde_encoder_register_frame_event_callback(struct drm_encoder *encoder,
void (*cb)(void *, u32), void *data);
/**
+ * sde_encoder_register_request_flip_callback - provide callback to encoder that
+ * will be called after HW flush is complete to request
+ * a page flip event from CRTC.
+ * @encoder: encoder pointer
+ * @cb: callback pointer, provide NULL to deregister
+ * @data: user data provided to callback
+ */
+void sde_encoder_register_request_flip_callback(struct drm_encoder *encoder,
+ void (*cb)(void *), void *data);
+
+/**
* sde_encoder_prepare_for_kickoff - schedule double buffer flip of the ctl
* path (i.e. ctl flush and start) at next appropriate time.
* Immediately: if no previous commit is outstanding.
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c
new file mode 100644
index 000000000000..69ab367307ea
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_edid_parser.c
@@ -0,0 +1,512 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm_edid.h>
+
+#include "sde_kms.h"
+#include "sde_edid_parser.h"
+
+/* TODO: copy from drm_edid.c and mdss_hdmi_edid.c. remove if using ELD */
+#define DBC_START_OFFSET 4
+#define EDID_DTD_LEN 18
+
+enum data_block_types {
+ RESERVED,
+ AUDIO_DATA_BLOCK,
+ VIDEO_DATA_BLOCK,
+ VENDOR_SPECIFIC_DATA_BLOCK,
+ SPEAKER_ALLOCATION_DATA_BLOCK,
+ VESA_DTC_DATA_BLOCK,
+ RESERVED2,
+ USE_EXTENDED_TAG
+};
+
+static u8 *sde_find_edid_extension(struct edid *edid, int ext_id)
+{
+ u8 *edid_ext = NULL;
+ int i;
+
+ /* No EDID or EDID extensions */
+ if (edid == NULL || edid->extensions == 0)
+ return NULL;
+
+ /* Find CEA extension */
+ for (i = 0; i < edid->extensions; i++) {
+ edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
+ if (edid_ext[0] == ext_id)
+ break;
+ }
+
+ if (i == edid->extensions)
+ return NULL;
+
+ return edid_ext;
+}
+
+static u8 *sde_find_cea_extension(struct edid *edid)
+{
+ return sde_find_edid_extension(edid, SDE_CEA_EXT);
+}
+
+static int
+sde_cea_db_payload_len(const u8 *db)
+{
+ return db[0] & 0x1f;
+}
+
+static int
+sde_cea_db_tag(const u8 *db)
+{
+ return db[0] >> 5;
+}
+
+static int
+sde_cea_revision(const u8 *cea)
+{
+ return cea[1];
+}
+
+static int
+sde_cea_db_offsets(const u8 *cea, int *start, int *end)
+{
+ /* Data block offset in CEA extension block */
+ *start = 4;
+ *end = cea[2];
+ if (*end == 0)
+ *end = 127;
+ if (*end < 4 || *end > 127)
+ return -ERANGE;
+ return 0;
+}
+
+#define sde_for_each_cea_db(cea, i, start, end) \
+for ((i) = (start); \
+(i) < (end) && (i) + sde_cea_db_payload_len(&(cea)[(i)]) < (end); \
+(i) += sde_cea_db_payload_len(&(cea)[(i)]) + 1)
+
+static u8 *sde_edid_find_extended_tag_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid) {
+ pr_err("%s: invalid input\n", __func__);
+ return NULL;
+ }
+
+ cea = sde_find_cea_extension(edid);
+
+ if (cea && sde_cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (sde_cea_db_offsets(cea, &start, &end))
+ return NULL;
+
+ sde_for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if ((sde_cea_db_tag(db) == SDE_EXTENDED_TAG) &&
+ (db[1] == blk_id))
+ return db;
+ }
+ }
+ return NULL;
+}
+
+static u8 *
+sde_edid_find_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid) {
+ pr_err("%s: invalid input\n", __func__);
+ return NULL;
+ }
+
+ cea = sde_find_cea_extension(edid);
+
+ if (cea && sde_cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (sde_cea_db_offsets(cea, &start, &end))
+ return 0;
+
+ sde_for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if (sde_cea_db_tag(db) == blk_id)
+ return db;
+ }
+ }
+ return NULL;
+}
+
+
+static const u8 *_sde_edid_find_block(const u8 *in_buf, u32 start_offset,
+ u8 type, u8 *len)
+{
+ /* the start of data block collection, start of Video Data Block */
+ u32 offset = start_offset;
+ u32 dbc_offset = in_buf[2];
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ /*
+ * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block
+ * collection present.
+ * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block
+ * collection present and no DTD data present.
+ */
+ if ((dbc_offset == 0) || (dbc_offset == 4)) {
+ SDE_ERROR("EDID: no DTD or non-DTD data present\n");
+ return NULL;
+ }
+
+ while (offset < dbc_offset) {
+ u8 block_len = in_buf[offset] & 0x1F;
+
+ if ((offset + block_len <= dbc_offset) &&
+ (in_buf[offset] >> 5) == type) {
+ *len = block_len;
+ SDE_EDID_DEBUG("block=%d found @ 0x%x w/ len=%d\n",
+ type, offset, block_len);
+
+ return in_buf + offset;
+ }
+ offset += 1 + block_len;
+ }
+
+ return NULL;
+}
+
+static void sde_edid_extract_vendor_id(struct sde_edid_ctrl *edid_ctrl)
+{
+ char *vendor_id;
+ u32 id_codes;
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ if (!edid_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ vendor_id = edid_ctrl->vendor_id;
+ id_codes = ((u32)edid_ctrl->edid->mfg_id[0] << 8) +
+ edid_ctrl->edid->mfg_id[1];
+
+ vendor_id[0] = 'A' - 1 + ((id_codes >> 10) & 0x1F);
+ vendor_id[1] = 'A' - 1 + ((id_codes >> 5) & 0x1F);
+ vendor_id[2] = 'A' - 1 + (id_codes & 0x1F);
+ vendor_id[3] = 0;
+ SDE_EDID_DEBUG("vendor id is %s ", vendor_id);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void sde_edid_set_y420_support(struct drm_connector *connector,
+u32 video_format)
+{
+ u8 cea_mode = 0;
+ struct drm_display_mode *mode;
+
+ /* Need to add Y420 support flag to the modes */
+ list_for_each_entry(mode, &connector->probed_modes, head) {
+ cea_mode = drm_match_cea_mode(mode);
+ if ((cea_mode != 0) && (cea_mode == video_format)) {
+ SDE_EDID_DEBUG("%s found match for %d ", __func__,
+ video_format);
+ mode->flags |= DRM_MODE_FLAG_SUPPORTS_YUV;
+ }
+ }
+}
+
+static void sde_edid_parse_Y420CMDB(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl,
+const u8 *db)
+{
+ u32 offset = 0;
+ u8 len = 0;
+ u8 svd_len = 0;
+ const u8 *svd = NULL;
+ u32 i = 0, j = 0;
+ u32 video_format = 0;
+
+ if (!edid_ctrl) {
+ DEV_ERR("%s: edid_ctrl is NULL\n", __func__);
+ return;
+ }
+
+ if (!db) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ len = db[0] & 0x1f;
+
+ if (len < 7)
+ return;
+ /* Byte 3 to L+1 contain SVDs */
+ offset += 2;
+
+ svd = sde_edid_find_block(edid_ctrl->edid, VIDEO_DATA_BLOCK);
+
+ if (svd) {
+ /*moving to the next byte as vic info begins there*/
+ ++svd;
+ svd_len = svd[0] & 0x1f;
+ }
+
+ for (i = 0; i < svd_len; i++, j++) {
+ video_format = *svd & 0x7F;
+ if (db[offset] & (1 << j))
+ sde_edid_set_y420_support(connector, video_format);
+
+ if (j & 0x80) {
+ j = j/8;
+ offset++;
+ if (offset >= len)
+ break;
+ }
+ }
+
+ SDE_EDID_DEBUG("%s -\n", __func__);
+
+}
+
+static void sde_edid_parse_Y420VDB(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl,
+const u8 *db)
+{
+ u8 len = db[0] & 0x1f;
+ u32 i = 0;
+ u32 video_format = 0;
+
+ if (!edid_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+
+ /* Offset to byte 3 */
+ db += 2;
+ for (i = 0; i < len - 1; i++) {
+ video_format = *(db + i) & 0x7F;
+ /*
+ * mode was already added in get_modes()
+ * only need to set the Y420 support flag
+ */
+ sde_edid_set_y420_support(connector, video_format);
+ }
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void sde_edid_set_mode_format(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl)
+{
+ const u8 *db = NULL;
+ struct drm_display_mode *mode;
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ /* Set YUV mode support flags for YCbcr420VDB */
+ db = sde_edid_find_extended_tag_block(edid_ctrl->edid,
+ Y420_VIDEO_DATA_BLOCK);
+ if (db)
+ sde_edid_parse_Y420VDB(connector, edid_ctrl, db);
+ else
+ SDE_EDID_DEBUG("YCbCr420 VDB is not present\n");
+
+ /* Set RGB supported on all modes where YUV is not set */
+ list_for_each_entry(mode, &connector->probed_modes, head) {
+ if (!(mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV))
+ mode->flags |= DRM_MODE_FLAG_SUPPORTS_RGB;
+ }
+
+
+ db = sde_edid_find_extended_tag_block(edid_ctrl->edid,
+ Y420_CAPABILITY_MAP_DATA_BLOCK);
+ if (db)
+ sde_edid_parse_Y420CMDB(connector, edid_ctrl, db);
+ else
+ SDE_EDID_DEBUG("YCbCr420 CMDB is not present\n");
+
+ SDE_EDID_DEBUG("%s -\n", __func__);
+}
+
+static void _sde_edid_extract_audio_data_blocks(
+ struct sde_edid_ctrl *edid_ctrl)
+{
+ u8 len = 0;
+ u8 adb_max = 0;
+ const u8 *adb = NULL;
+ u32 offset = DBC_START_OFFSET;
+ u8 *cea = NULL;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("invalid edid_ctrl\n");
+ return;
+ }
+ SDE_EDID_DEBUG("%s +", __func__);
+ cea = sde_find_cea_extension(edid_ctrl->edid);
+ if (!cea) {
+ SDE_DEBUG("CEA extension not found\n");
+ return;
+ }
+
+ edid_ctrl->adb_size = 0;
+
+ memset(edid_ctrl->audio_data_block, 0,
+ sizeof(edid_ctrl->audio_data_block));
+
+ do {
+ len = 0;
+ adb = _sde_edid_find_block(cea, offset, AUDIO_DATA_BLOCK,
+ &len);
+
+ if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE ||
+ adb_max >= MAX_NUMBER_ADB)) {
+ if (!edid_ctrl->adb_size) {
+ SDE_DEBUG("No/Invalid Audio Data Block\n");
+ return;
+ }
+
+ continue;
+ }
+
+ memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size,
+ adb + 1, len);
+ offset = (adb - cea) + 1 + len;
+
+ edid_ctrl->adb_size += len;
+ adb_max++;
+ } while (adb);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void _sde_edid_extract_speaker_allocation_data(
+ struct sde_edid_ctrl *edid_ctrl)
+{
+ u8 len;
+ const u8 *sadb = NULL;
+ u8 *cea = NULL;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("invalid edid_ctrl\n");
+ return;
+ }
+ SDE_EDID_DEBUG("%s +", __func__);
+ cea = sde_find_cea_extension(edid_ctrl->edid);
+ if (!cea) {
+ SDE_DEBUG("CEA extension not found\n");
+ return;
+ }
+
+ sadb = _sde_edid_find_block(cea, DBC_START_OFFSET,
+ SPEAKER_ALLOCATION_DATA_BLOCK, &len);
+ if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) {
+ SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n");
+ return;
+ }
+
+ memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len);
+ edid_ctrl->sadb_size = len;
+
+ SDE_EDID_DEBUG("speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n",
+ sadb[1],
+ (sadb[1] & BIT(0)) ? "FL/FR," : "",
+ (sadb[1] & BIT(1)) ? "LFE," : "",
+ (sadb[1] & BIT(2)) ? "FC," : "",
+ (sadb[1] & BIT(3)) ? "RL/RR," : "",
+ (sadb[1] & BIT(4)) ? "RC," : "",
+ (sadb[1] & BIT(5)) ? "FLC/FRC," : "",
+ (sadb[1] & BIT(6)) ? "RLC/RRC," : "");
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+struct sde_edid_ctrl *sde_edid_init(void)
+{
+ struct sde_edid_ctrl *edid_ctrl = NULL;
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ edid_ctrl = kzalloc(sizeof(*edid_ctrl), GFP_KERNEL);
+ if (!edid_ctrl) {
+ SDE_ERROR("edid_ctrl alloc failed\n");
+ return NULL;
+ }
+ memset((edid_ctrl), 0, sizeof(*edid_ctrl));
+ SDE_EDID_DEBUG("%s -\n", __func__);
+ return edid_ctrl;
+}
+
+void sde_free_edid(void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ kfree(edid_ctrl->edid);
+ edid_ctrl->edid = NULL;
+}
+
+void sde_edid_deinit(void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ sde_free_edid((void *)&edid_ctrl);
+ kfree(edid_ctrl);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+int _sde_edid_update_modes(struct drm_connector *connector,
+ void *input)
+{
+ int rc = 0;
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ if (edid_ctrl->edid) {
+ drm_mode_connector_update_edid_property(connector,
+ edid_ctrl->edid);
+
+ rc = drm_add_edid_modes(connector, edid_ctrl->edid);
+ sde_edid_set_mode_format(connector, edid_ctrl);
+ SDE_EDID_DEBUG("%s -", __func__);
+ return rc;
+ }
+
+ drm_mode_connector_update_edid_property(connector, NULL);
+ SDE_EDID_DEBUG("%s null edid -", __func__);
+ return rc;
+}
+
+bool sde_detect_hdmi_monitor(void *input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
+
+ return drm_detect_hdmi_monitor(edid_ctrl->edid);
+}
+
+void sde_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter, void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ edid_ctrl->edid = drm_get_edid(connector, adapter);
+ SDE_EDID_DEBUG("%s +\n", __func__);
+
+ if (!edid_ctrl->edid)
+ SDE_ERROR("EDID read failed\n");
+
+ if (edid_ctrl->edid) {
+ sde_edid_extract_vendor_id(edid_ctrl);
+ _sde_edid_extract_audio_data_blocks(edid_ctrl);
+ _sde_edid_extract_speaker_allocation_data(edid_ctrl);
+ }
+ SDE_EDID_DEBUG("%s -\n", __func__);
+};
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.h b/drivers/gpu/drm/msm/sde_edid_parser.h
new file mode 100644
index 000000000000..1143dc2c7bec
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_edid_parser.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SDE_EDID_PARSER_H_
+#define _SDE_EDID_PARSER_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+
+
+#define MAX_NUMBER_ADB 5
+#define MAX_AUDIO_DATA_BLOCK_SIZE 30
+#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3
+#define EDID_VENDOR_ID_SIZE 4
+
+#define SDE_CEA_EXT 0x02
+#define SDE_EXTENDED_TAG 0x07
+
+enum extended_data_block_types {
+ VIDEO_CAPABILITY_DATA_BLOCK = 0x0,
+ VENDOR_SPECIFIC_VIDEO_DATA_BLOCK = 0x01,
+ HDMI_VIDEO_DATA_BLOCK = 0x04,
+ HDR_STATIC_METADATA_DATA_BLOCK = 0x06,
+ Y420_VIDEO_DATA_BLOCK = 0x0E,
+ VIDEO_FORMAT_PREFERENCE_DATA_BLOCK = 0x0D,
+ Y420_CAPABILITY_MAP_DATA_BLOCK = 0x0F,
+ VENDOR_SPECIFIC_AUDIO_DATA_BLOCK = 0x11,
+ INFOFRAME_DATA_BLOCK = 0x20,
+};
+
+#ifdef SDE_EDID_DEBUG_ENABLE
+#define SDE_EDID_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
+#else
+#define SDE_EDID_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
+#endif
+
+/*
+ * struct hdmi_edid_hdr_data - HDR Static Metadata
+ * @eotf: Electro-Optical Transfer Function
+ * @metadata_type_one: Static Metadata Type 1 support
+ * @max_luminance: Desired Content Maximum Luminance
+ * @avg_luminance: Desired Content Frame-average Luminance
+ * @min_luminance: Desired Content Minimum Luminance
+ */
+struct sde_edid_hdr_data {
+ u32 eotf;
+ bool metadata_type_one;
+ u32 max_luminance;
+ u32 avg_luminance;
+ u32 min_luminance;
+};
+
+struct sde_edid_sink_caps {
+ u32 max_pclk_in_hz;
+ bool scdc_present;
+ bool scramble_support; /* scramble support for less than 340Mcsc */
+ bool read_req_support;
+ bool osd_disparity;
+ bool dual_view_support;
+ bool ind_view_support;
+};
+
+struct sde_edid_ctrl {
+ struct edid *edid;
+ u8 pt_scan_info;
+ u8 it_scan_info;
+ u8 ce_scan_info;
+ u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE];
+ int adb_size;
+ u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
+ int sadb_size;
+ bool hdr_supported;
+ char vendor_id[EDID_VENDOR_ID_SIZE];
+ struct sde_edid_sink_caps sink_caps;
+ struct sde_edid_hdr_data hdr_data;
+};
+
+/**
+ * sde_edid_init() - init edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ * Return: handle to sde_edid_ctrl for the client.
+ */
+struct sde_edid_ctrl *sde_edid_init(void);
+
+/**
+ * sde_edid_deinit() - deinit edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_edid_deinit(void **edid_ctrl);
+
+/**
+ * sde_get_edid() - get edid info.
+ * @connector: Handle to the drm_connector.
+ * @adapter: handle to i2c adapter for DDC read
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_get_edid(struct drm_connector *connector,
+struct i2c_adapter *adapter,
+void **edid_ctrl);
+
+/**
+ * sde_free_edid() - free edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_free_edid(void **edid_ctrl);
+
+/**
+ * sde_detect_hdmi_monitor() - detect HDMI mode.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: error code.
+ */
+bool sde_detect_hdmi_monitor(void *edid_ctrl);
+
+/**
+ * _sde_edid_update_modes() - populate EDID modes.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: error code.
+ */
+int _sde_edid_update_modes(struct drm_connector *connector,
+ void *edid_ctrl);
+
+#endif /* _SDE_EDID_PARSER_H_ */
+
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index f084ca9a62a1..54b8da5302e8 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -1041,6 +1041,13 @@ static void _set_ft_policy(struct adreno_device *adreno_dev,
*/
if (drawctxt->base.flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE)
set_bit(KGSL_FT_DISABLE, &cmdobj->fault_policy);
+ /*
+ * Set the fault tolerance policy to FT_REPLAY - As context wants
+ * to invalidate it after a replay attempt fails. This doesn't
+ * require to execute the default FT policy.
+ */
+ else if (drawctxt->base.flags & KGSL_CONTEXT_INVALIDATE_ON_FAULT)
+ set_bit(KGSL_FT_REPLAY, &cmdobj->fault_policy);
else
cmdobj->fault_policy = adreno_dev->ft_policy;
}
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 21a6399ba38e..b8ae24bc3935 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -346,6 +346,7 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv,
KGSL_CONTEXT_PER_CONTEXT_TS |
KGSL_CONTEXT_USER_GENERATED_TS |
KGSL_CONTEXT_NO_FAULT_TOLERANCE |
+ KGSL_CONTEXT_INVALIDATE_ON_FAULT |
KGSL_CONTEXT_CTX_SWITCH |
KGSL_CONTEXT_PRIORITY_MASK |
KGSL_CONTEXT_TYPE_MASK |
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 1de8e212a703..986026aa1b66 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -260,9 +260,12 @@ kgsl_mem_entry_create(void)
{
struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
- if (entry != NULL)
+ if (entry != NULL) {
kref_init(&entry->refcount);
+ /* put this ref in the caller functions after init */
+ kref_get(&entry->refcount);
+ }
return entry;
}
#ifdef CONFIG_DMA_SHARED_BUFFER
@@ -1764,9 +1767,9 @@ long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv,
/* Commit the pointer to the context in context_idr */
write_lock(&device->context_lock);
idr_replace(&device->context_idr, context, context->id);
+ param->drawctxt_id = context->id;
write_unlock(&device->context_lock);
- param->drawctxt_id = context->id;
done:
return result;
}
@@ -2399,6 +2402,9 @@ long kgsl_ioctl_gpuobj_import(struct kgsl_device_private *dev_priv,
trace_kgsl_mem_map(entry, fd);
kgsl_mem_entry_commit_process(entry);
+
+ /* put the extra refcount for kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
return 0;
unmap:
@@ -2705,6 +2711,9 @@ long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
trace_kgsl_mem_map(entry, param->fd);
kgsl_mem_entry_commit_process(entry);
+
+ /* put the extra refcount for kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
return result;
error_attach:
@@ -3143,6 +3152,9 @@ long kgsl_ioctl_gpuobj_alloc(struct kgsl_device_private *dev_priv,
param->mmapsize = kgsl_memdesc_footprint(&entry->memdesc);
param->id = entry->id;
+ /* put the extra refcount for kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
}
@@ -3166,6 +3178,9 @@ long kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv,
param->size = (size_t) entry->memdesc.size;
param->flags = (unsigned int) entry->memdesc.flags;
+ /* put the extra refcount for kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
}
@@ -3189,6 +3204,9 @@ long kgsl_ioctl_gpumem_alloc_id(struct kgsl_device_private *dev_priv,
param->mmapsize = (size_t) kgsl_memdesc_footprint(&entry->memdesc);
param->gpuaddr = (unsigned long) entry->memdesc.gpuaddr;
+ /* put the extra refcount for kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
}
@@ -3306,6 +3324,9 @@ long kgsl_ioctl_sparse_phys_alloc(struct kgsl_device_private *dev_priv,
trace_sparse_phys_alloc(entry->id, param->size, param->pagesize);
kgsl_mem_entry_commit_process(entry);
+ /* put the extra refcount for kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
err_invalid_pages:
@@ -3385,6 +3406,9 @@ long kgsl_ioctl_sparse_virt_alloc(struct kgsl_device_private *dev_priv,
trace_sparse_virt_alloc(entry->id, param->size, param->pagesize);
kgsl_mem_entry_commit_process(entry);
+ /* put the extra refcount for kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
}
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 177b283a2dda..d93fd9bfbcd0 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -77,6 +77,7 @@ enum kgsl_event_results {
{ KGSL_CONTEXT_PER_CONTEXT_TS, "PER_CONTEXT_TS" }, \
{ KGSL_CONTEXT_USER_GENERATED_TS, "USER_TS" }, \
{ KGSL_CONTEXT_NO_FAULT_TOLERANCE, "NO_FT" }, \
+ { KGSL_CONTEXT_INVALIDATE_ON_FAULT, "INVALIDATE_ON_FAULT" }, \
{ KGSL_CONTEXT_PWR_CONSTRAINT, "PWR" }, \
{ KGSL_CONTEXT_SAVE_GMEM, "SAVE_GMEM" }
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 57c191798699..ddbf7e7e0d98 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -274,7 +274,7 @@ cleanup:
*
* This routine is called normally during driver unloading or exiting.
*/
-void hv_cleanup(void)
+void hv_cleanup(bool crash)
{
union hv_x64_msr_hypercall_contents hypercall_msr;
@@ -284,7 +284,8 @@ void hv_cleanup(void)
if (hv_context.hypercall_page) {
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
- vfree(hv_context.hypercall_page);
+ if (!crash)
+ vfree(hv_context.hypercall_page);
hv_context.hypercall_page = NULL;
}
@@ -304,7 +305,8 @@ void hv_cleanup(void)
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64);
- vfree(hv_context.tsc_page);
+ if (!crash)
+ vfree(hv_context.tsc_page);
hv_context.tsc_page = NULL;
}
#endif
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index b853b4b083bd..43af91362be5 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -714,7 +714,7 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
* If the pfn range we are dealing with is not in the current
* "hot add block", move on.
*/
- if ((start_pfn >= has->end_pfn))
+ if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue;
/*
* If the current hot add-request extends beyond
@@ -768,7 +768,7 @@ static unsigned long handle_pg_range(unsigned long pg_start,
* If the pfn range we are dealing with is not in the current
* "hot add block", move on.
*/
- if ((start_pfn >= has->end_pfn))
+ if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue;
old_covered_state = has->covered_end_pfn;
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 12156db2e88e..75e383e6d03d 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -581,7 +581,7 @@ struct hv_ring_buffer_debug_info {
extern int hv_init(void);
-extern void hv_cleanup(void);
+extern void hv_cleanup(bool crash);
extern int hv_post_message(union hv_connection_id connection_id,
enum hv_message_type message_type,
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 509ed9731630..802dcb409030 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -889,7 +889,7 @@ err_alloc:
bus_unregister(&hv_bus);
err_cleanup:
- hv_cleanup();
+ hv_cleanup(false);
return ret;
}
@@ -1254,7 +1254,7 @@ static void hv_kexec_handler(void)
vmbus_initiate_unload();
for_each_online_cpu(cpu)
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
- hv_cleanup();
+ hv_cleanup(false);
};
static void hv_crash_handler(struct pt_regs *regs)
@@ -1266,7 +1266,7 @@ static void hv_crash_handler(struct pt_regs *regs)
* for kdump.
*/
hv_synic_cleanup(NULL);
- hv_cleanup();
+ hv_cleanup(true);
};
static int __init hv_acpi_init(void)
@@ -1330,7 +1330,7 @@ static void __exit vmbus_exit(void)
&hyperv_panic_block);
}
bus_unregister(&hv_bus);
- hv_cleanup();
+ hv_cleanup(false);
for_each_online_cpu(cpu) {
tasklet_kill(hv_context.event_dpc[cpu]);
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 287d839f98d0..9f9dd574c8d0 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2557,6 +2557,8 @@ static int etm4_set_reg_dump(struct etmv4_drvdata *drvdata)
drvdata->reg_data.addr = virt_to_phys(baddr);
drvdata->reg_data.len = size;
+ scnprintf(drvdata->reg_data.name, sizeof(drvdata->reg_data.name),
+ "KETM_REG%d", drvdata->cpu);
dump_entry.id = MSM_DUMP_DATA_ETM_REG + drvdata->cpu;
dump_entry.addr = virt_to_phys(&drvdata->reg_data);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 34b12e015768..c5998bd5ce02 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2016-2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1726,6 +1726,9 @@ static int tmc_etf_set_buf_dump(struct tmc_drvdata *drvdata)
drvdata->buf_data.addr = virt_to_phys(drvdata->buf);
drvdata->buf_data.len = drvdata->size;
+ scnprintf(drvdata->buf_data.name, sizeof(drvdata->buf_data.name),
+ "KTMC_ETF%d", count);
+
dump_entry.id = MSM_DUMP_DATA_TMC_ETF + count;
dump_entry.addr = virt_to_phys(&drvdata->buf_data);
@@ -1817,6 +1820,8 @@ static int tmc_set_reg_dump(struct tmc_drvdata *drvdata)
drvdata->reg_data.addr = virt_to_phys(drvdata->reg_buf);
drvdata->reg_data.len = size;
+ scnprintf(drvdata->reg_data.name, sizeof(drvdata->reg_data.name),
+ "KTMC_REG%d", count);
dump_entry.id = MSM_DUMP_DATA_TMC_REG + count;
dump_entry.addr = virt_to_phys(&drvdata->reg_data);
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index 0470fc843d4e..9b6854607d73 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -151,7 +151,9 @@ static irqreturn_t tiadc_irq_h(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct tiadc_device *adc_dev = iio_priv(indio_dev);
- unsigned int status, config;
+ unsigned int status, config, adc_fsm;
+ unsigned short count = 0;
+
status = tiadc_readl(adc_dev, REG_IRQSTATUS);
/*
@@ -165,6 +167,15 @@ static irqreturn_t tiadc_irq_h(int irq, void *private)
tiadc_writel(adc_dev, REG_CTRL, config);
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN
| IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES);
+
+ /* wait for idle state.
+ * ADC needs to finish the current conversion
+ * before disabling the module
+ */
+ do {
+ adc_fsm = tiadc_readl(adc_dev, REG_ADCFSM);
+ } while (adc_fsm != 0x10 && count++ < 100);
+
tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
return IRQ_HANDLED;
} else if (status & IRQENB_FIFO1THRES) {
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 595511022795..0a86ef43e781 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -51,8 +51,6 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
st->report_state.report_id,
st->report_state.index,
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
-
- poll_value = hid_sensor_read_poll_value(st);
} else {
int val;
@@ -89,7 +87,9 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
sizeof(state_val), &state_val);
- if (state && poll_value)
+ if (state)
+ poll_value = hid_sensor_read_poll_value(st);
+ if (poll_value > 0)
msleep_interruptible(poll_value * 2);
return 0;
diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c
index d96aa27dfcdc..db64adfbe1af 100644
--- a/drivers/input/joystick/iforce/iforce-usb.c
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -141,6 +141,9 @@ static int iforce_usb_probe(struct usb_interface *intf,
interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
epirq = &interface->endpoint[0].desc;
epout = &interface->endpoint[1].desc;
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 9365535ba7f1..50a7faa504f7 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -675,6 +675,10 @@ static int cm109_usb_probe(struct usb_interface *intf,
int error = -ENOMEM;
interface = intf->cur_altsetting;
+
+ if (interface->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c
index 1df5d8812991..21898c308075 100644
--- a/drivers/input/misc/hbtp_input.c
+++ b/drivers/input/misc/hbtp_input.c
@@ -1,5 +1,5 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -47,6 +47,8 @@ struct hbtp_data {
struct input_dev *input_dev;
s32 count;
struct mutex mutex;
+ struct mutex sensormutex;
+ struct hbtp_sensor_data *sensor_data;
bool touch_status[HBTP_MAX_FINGER];
#if defined(CONFIG_FB)
struct notifier_block fb_notif;
@@ -87,10 +89,14 @@ struct hbtp_data {
u32 power_on_delay;
u32 power_off_delay;
bool manage_pin_ctrl;
+ s16 ROI[MAX_ROI_SIZE];
+ s16 accelBuffer[MAX_ACCEL_SIZE];
};
static struct hbtp_data *hbtp;
+static struct kobject *sensor_kobject;
+
#if defined(CONFIG_FB)
static int hbtp_fb_suspend(struct hbtp_data *ts);
static int hbtp_fb_early_resume(struct hbtp_data *ts);
@@ -103,10 +109,27 @@ static int fb_notifier_callback(struct notifier_block *self,
{
int blank;
struct fb_event *evdata = data;
+ struct fb_info *fbi = NULL;
struct hbtp_data *hbtp_data =
container_of(self, struct hbtp_data, fb_notif);
- if (evdata && evdata->data && hbtp_data &&
+ if (!evdata) {
+ pr_debug("evdata is NULL");
+ return 0;
+ }
+ fbi = evdata->info;
+
+ /*
+ * Node 0 is the primary display and others are
+ * external displays such as HDMI/DP.
+ * We need to handle only fb event for the primary display.
+ */
+ if (!fbi || fbi->node != 0) {
+ pr_debug("%s: no need to handle the fb event", __func__);
+ return 0;
+ }
+
+ if (evdata->data && hbtp_data &&
(event == FB_EARLY_EVENT_BLANK ||
event == FB_R_EARLY_EVENT_BLANK)) {
blank = *(int *)(evdata->data);
@@ -135,7 +158,7 @@ static int fb_notifier_callback(struct notifier_block *self,
}
}
- if (evdata && evdata->data && hbtp_data &&
+ if (evdata->data && hbtp_data &&
event == FB_EVENT_BLANK) {
blank = *(int *)(evdata->data);
if (blank == FB_BLANK_POWERDOWN) {
@@ -150,6 +173,46 @@ static int fb_notifier_callback(struct notifier_block *self,
}
#endif
+static ssize_t hbtp_sensor_roi_show(struct file *dev, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t pos,
+ size_t size) {
+ mutex_lock(&hbtp->sensormutex);
+ memcpy(buf, hbtp->ROI, size);
+ mutex_unlock(&hbtp->sensormutex);
+
+ return size;
+}
+
+static ssize_t hbtp_sensor_vib_show(struct file *dev, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t pos,
+ size_t size) {
+ mutex_lock(&hbtp->sensormutex);
+ memcpy(buf, hbtp->accelBuffer, size);
+ mutex_unlock(&hbtp->sensormutex);
+
+ return size;
+}
+
+static struct bin_attribute capdata_attr = {
+ .attr = {
+ .name = "capdata",
+ .mode = S_IRUGO,
+ },
+ .size = 1024,
+ .read = hbtp_sensor_roi_show,
+ .write = NULL,
+};
+
+static struct bin_attribute vibdata_attr = {
+ .attr = {
+ .name = "vib_data",
+ .mode = S_IRUGO,
+ },
+ .size = MAX_ACCEL_SIZE*sizeof(int16_t),
+ .read = hbtp_sensor_vib_show,
+ .write = NULL,
+};
+
static int hbtp_input_open(struct inode *inode, struct file *file)
{
mutex_lock(&hbtp->mutex);
@@ -748,6 +811,22 @@ static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd,
return -EINVAL;
}
break;
+
+ case HBTP_SET_SENSORDATA:
+ if (copy_from_user(hbtp->sensor_data, (void *)arg,
+ sizeof(struct hbtp_sensor_data))) {
+ pr_err("%s: Error copying data\n", __func__);
+ return -EFAULT;
+ }
+ mutex_lock(&hbtp->sensormutex);
+ memcpy(hbtp->ROI, hbtp->sensor_data->ROI, sizeof(hbtp->ROI));
+ memcpy(hbtp->accelBuffer, hbtp->sensor_data->accelBuffer,
+ sizeof(hbtp->accelBuffer));
+ mutex_unlock(&hbtp->sensormutex);
+
+ error = 0;
+ break;
+
default:
pr_err("%s: Unsupported ioctl command %u\n", __func__, cmd);
error = -EINVAL;
@@ -1358,7 +1437,13 @@ static int __init hbtp_init(void)
if (!hbtp)
return -ENOMEM;
+ hbtp->sensor_data = kzalloc(sizeof(struct hbtp_sensor_data),
+ GFP_KERNEL);
+ if (!hbtp->sensor_data)
+ goto err_sensordata;
+
mutex_init(&hbtp->mutex);
+ mutex_init(&hbtp->sensormutex);
error = misc_register(&hbtp_input_misc);
if (error) {
@@ -1376,6 +1461,28 @@ static int __init hbtp_init(void)
}
#endif
+ sensor_kobject = kobject_create_and_add("hbtpsensor", kernel_kobj);
+ if (!sensor_kobject) {
+ pr_err("%s: Could not create hbtpsensor kobject\n", __func__);
+ goto err_kobject_create;
+ }
+
+ error = sysfs_create_bin_file(sensor_kobject, &capdata_attr);
+ if (error < 0) {
+ pr_err("%s: hbtp capdata sysfs creation failed: %d\n", __func__,
+ error);
+ goto err_sysfs_create_capdata;
+ }
+ pr_debug("capdata sysfs creation success\n");
+
+ error = sysfs_create_bin_file(sensor_kobject, &vibdata_attr);
+ if (error < 0) {
+ pr_err("%s: vibdata sysfs creation failed: %d\n", __func__,
+ error);
+ goto err_sysfs_create_vibdata;
+ }
+ pr_debug("vibdata sysfs creation success\n");
+
error = platform_driver_register(&hbtp_pdev_driver);
if (error) {
pr_err("Failed to register platform driver: %d\n", error);
@@ -1385,12 +1492,20 @@ static int __init hbtp_init(void)
return 0;
err_platform_drv_reg:
+ sysfs_remove_bin_file(sensor_kobject, &vibdata_attr);
+err_sysfs_create_vibdata:
+ sysfs_remove_bin_file(sensor_kobject, &capdata_attr);
+err_sysfs_create_capdata:
+ kobject_put(sensor_kobject);
+err_kobject_create:
#if defined(CONFIG_FB)
fb_unregister_client(&hbtp->fb_notif);
err_fb_reg:
#endif
misc_deregister(&hbtp_input_misc);
err_misc_reg:
+ kfree(hbtp->sensor_data);
+err_sensordata:
kfree(hbtp);
return error;
@@ -1398,6 +1513,9 @@ err_misc_reg:
static void __exit hbtp_exit(void)
{
+ sysfs_remove_bin_file(sensor_kobject, &vibdata_attr);
+ sysfs_remove_bin_file(sensor_kobject, &capdata_attr);
+ kobject_put(sensor_kobject);
misc_deregister(&hbtp_input_misc);
if (hbtp->input_dev)
input_unregister_device(hbtp->input_dev);
@@ -1408,6 +1526,7 @@ static void __exit hbtp_exit(void)
platform_driver_unregister(&hbtp_pdev_driver);
+ kfree(hbtp->sensor_data);
kfree(hbtp);
}
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index 9c0ea36913b4..f4e8fbec6a94 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -1667,6 +1667,10 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc
return -EINVAL;
alt = pcu->ctrl_intf->cur_altsetting;
+
+ if (alt->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
pcu->ep_ctrl = &alt->endpoint[0].desc;
pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl);
diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c
index add11d47ea2f..339f94c072f4 100644
--- a/drivers/input/misc/qpnp-power-on.c
+++ b/drivers/input/misc/qpnp-power-on.c
@@ -207,7 +207,7 @@ struct qpnp_pon {
int pon_power_off_reason;
int num_pon_reg;
int num_pon_config;
- u32 dbc;
+ u32 dbc_time_us;
u32 uvlo;
int warm_reset_poff_type;
int hard_reset_poff_type;
@@ -219,6 +219,8 @@ struct qpnp_pon {
u8 warm_reset_reason2;
bool is_spon;
bool store_hard_reset_reason;
+ bool kpdpwr_dbc_enable;
+ ktime_t kpdpwr_last_release_time;
};
static int pon_ship_mode_en;
@@ -381,7 +383,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay)
int rc = 0;
u32 val;
- if (delay == pon->dbc)
+ if (delay == pon->dbc_time_us)
goto out;
if (pon->pon_input)
@@ -409,7 +411,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay)
goto unlock;
}
- pon->dbc = delay;
+ pon->dbc_time_us = delay;
unlock:
if (pon->pon_input)
@@ -418,12 +420,34 @@ out:
return rc;
}
+static int qpnp_pon_get_dbc(struct qpnp_pon *pon, u32 *delay)
+{
+ int rc;
+ unsigned int val;
+
+ rc = regmap_read(pon->regmap, QPNP_PON_DBC_CTL(pon), &val);
+ if (rc) {
+ pr_err("Unable to read pon_dbc_ctl rc=%d\n", rc);
+ return rc;
+ }
+ val &= QPNP_PON_DBC_DELAY_MASK(pon);
+
+ if (is_pon_gen2(pon))
+ *delay = USEC_PER_SEC /
+ (1 << (QPNP_PON_GEN2_DELAY_BIT_SHIFT - val));
+ else
+ *delay = USEC_PER_SEC /
+ (1 << (QPNP_PON_DELAY_BIT_SHIFT - val));
+
+ return rc;
+}
+
static ssize_t qpnp_pon_dbc_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qpnp_pon *pon = dev_get_drvdata(dev);
- return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc);
+ return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc_time_us);
}
static ssize_t qpnp_pon_dbc_store(struct device *dev,
@@ -777,6 +801,7 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
u8 pon_rt_bit = 0;
u32 key_status;
uint pon_rt_sts;
+ u64 elapsed_us;
cfg = qpnp_get_cfg(pon, pon_type);
if (!cfg)
@@ -786,6 +811,15 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
if (!cfg->key_code)
return 0;
+ if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) {
+ elapsed_us = ktime_us_delta(ktime_get(),
+ pon->kpdpwr_last_release_time);
+ if (elapsed_us < pon->dbc_time_us) {
+ pr_debug("Ignoring kpdpwr event - within debounce time\n");
+ return 0;
+ }
+ }
+
/* check the RT status to get the current status of the line */
rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts);
if (rc) {
@@ -814,6 +848,11 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
cfg->key_code, pon_rt_sts);
key_status = pon_rt_sts & pon_rt_bit;
+ if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) {
+ if (!key_status)
+ pon->kpdpwr_last_release_time = ktime_get();
+ }
+
/*
* simulate press event in case release event occurred
* without a press event
@@ -2233,8 +2272,22 @@ static int qpnp_pon_probe(struct platform_device *pdev)
}
} else {
rc = qpnp_pon_set_dbc(pon, delay);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Unable to set PON debounce delay rc=%d\n", rc);
+ return rc;
+ }
+ }
+ rc = qpnp_pon_get_dbc(pon, &pon->dbc_time_us);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Unable to get PON debounce delay rc=%d\n", rc);
+ return rc;
}
+ pon->kpdpwr_dbc_enable = of_property_read_bool(pon->pdev->dev.of_node,
+ "qcom,kpdpwr-sw-debounce");
+
rc = of_property_read_u32(pon->pdev->dev.of_node,
"qcom,warm-reset-poweroff-type",
&pon->warm_reset_poff_type);
diff --git a/drivers/input/misc/vl53L0/stmvl53l0-cci.h b/drivers/input/misc/vl53L0/stmvl53l0-cci.h
index 51477701cb1d..b99ffe783e59 100644
--- a/drivers/input/misc/vl53L0/stmvl53l0-cci.h
+++ b/drivers/input/misc/vl53L0/stmvl53l0-cci.h
@@ -51,11 +51,12 @@ struct cci_data {
struct msm_camera_gpio_conf *gconf;
struct msm_pinctrl_info pinctrl_info;
uint8_t cam_pinctrl_status;
-
};
+
int stmvl53l0_init_cci(void);
void stmvl53l0_exit_cci(void *);
int stmvl53l0_power_down_cci(void *);
int stmvl53l0_power_up_cci(void *, unsigned int *);
+int stmvl53l0_cci_power_status(void *);
#endif /* CAMERA_CCI */
#endif /* STMVL53L0_CCI_H */
diff --git a/drivers/input/misc/vl53L0/stmvl53l0.h b/drivers/input/misc/vl53L0/stmvl53l0.h
index b15d78cc6825..b6ef547e86e7 100644
--- a/drivers/input/misc/vl53L0/stmvl53l0.h
+++ b/drivers/input/misc/vl53L0/stmvl53l0.h
@@ -208,6 +208,7 @@ struct stmvl53l0_module_fn_t {
void (*deinit)(void *);
int (*power_up)(void *, unsigned int *);
int (*power_down)(void *);
+ int (*query_power_status)(void *);
};
diff --git a/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c b/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c
index 79fba00ea086..fecafe8db949 100644
--- a/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c
+++ b/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c
@@ -187,8 +187,15 @@ static const struct v4l2_subdev_internal_ops msm_tof_internal_ops = {
static long msm_tof_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
- vl53l0_dbgmsg("Subdev_ioctl not handled\n");
- return 0;
+ struct cci_data *cci_object = NULL;
+ int32_t rc = 0;
+
+ cci_object = v4l2_get_subdevdata(sd);
+ if (cmd == MSM_SD_SHUTDOWN)
+ cci_object->power_up = 0;
+
+ vl53l0_dbgmsg("cmd = %d power_up = %d", cmd, cci_object->power_up);
+ return rc;
}
static int32_t msm_tof_power(struct v4l2_subdev *sd, int on)
@@ -493,6 +500,13 @@ int stmvl53l0_power_down_cci(void *cci_object)
return ret;
}
+int stmvl53l0_cci_power_status(void *cci_object)
+{
+ struct cci_data *data = (struct cci_data *)cci_object;
+
+ return data->power_up;
+}
+
int stmvl53l0_init_cci(void)
{
int ret = 0;
diff --git a/drivers/input/misc/vl53L0/stmvl53l0_module.c b/drivers/input/misc/vl53L0/stmvl53l0_module.c
index 6881aba9fc64..c46be43a03e5 100644
--- a/drivers/input/misc/vl53L0/stmvl53l0_module.c
+++ b/drivers/input/misc/vl53L0/stmvl53l0_module.c
@@ -39,7 +39,6 @@
#include "vl53l010_api.h"
#define USE_INT
-
/* #define DEBUG_TIME_LOG */
#ifdef DEBUG_TIME_LOG
struct timeval start_tv, stop_tv;
@@ -55,6 +54,7 @@ static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = {
.deinit = stmvl53l0_exit_cci,
.power_up = stmvl53l0_power_up_cci,
.power_down = stmvl53l0_power_down_cci,
+ .query_power_status = stmvl53l0_cci_power_status,
};
#else
static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = {
@@ -62,6 +62,7 @@ static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = {
.deinit = stmvl53l0_exit_i2c,
.power_up = stmvl53l0_power_up_i2c,
.power_down = stmvl53l0_power_down_i2c,
+ .stmv53l0_cci_power_status = NULL;
};
#endif
struct stmvl53l0_module_fn_t *pmodule_func_tbl;
@@ -956,6 +957,12 @@ static void stmvl53l0_work_handler(struct work_struct *work)
mutex_lock(&data->work_mutex);
/* vl53l0_dbgmsg("Enter\n"); */
+ if (pmodule_func_tbl->query_power_status(data->client_object) == 0) {
+ if (data->enable_ps_sensor == 1) {
+ stmvl53l0_stop(data);
+ data->enable_ps_sensor = 0;
+ }
+ }
if (vl53l0_dev->enable_ps_sensor == 1) {
#ifdef DEBUG_TIME_LOG
diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c
index 79c964c075f1..6e7ff9561d92 100644
--- a/drivers/input/misc/yealink.c
+++ b/drivers/input/misc/yealink.c
@@ -875,6 +875,10 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
int ret, pipe, i;
interface = intf->cur_altsetting;
+
+ if (interface->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index ed1935f300a7..da5458dfb1e3 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -218,17 +218,19 @@ static int elan_query_product(struct elan_tp_data *data)
static int elan_check_ASUS_special_fw(struct elan_tp_data *data)
{
- if (data->ic_type != 0x0E)
- return false;
-
- switch (data->product_id) {
- case 0x05 ... 0x07:
- case 0x09:
- case 0x13:
+ if (data->ic_type == 0x0E) {
+ switch (data->product_id) {
+ case 0x05 ... 0x07:
+ case 0x09:
+ case 0x13:
+ return true;
+ }
+ } else if (data->ic_type == 0x08 && data->product_id == 0x26) {
+ /* ASUS EeeBook X205TA */
return true;
- default:
- return false;
}
+
+ return false;
}
static int __elan_initialize(struct elan_tp_data *data)
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 0cdd95801a25..25eab453f2b2 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -120,6 +120,13 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
},
},
{
+ /* Dell Embedded Box PC 3000 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"),
+ },
+ },
+ {
/* OQO Model 01 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c
index cd852059b99e..df4bea96d7ed 100644
--- a/drivers/input/tablet/hanwang.c
+++ b/drivers/input/tablet/hanwang.c
@@ -340,6 +340,9 @@ static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id
int error;
int i;
+ if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL);
input_dev = input_allocate_device();
if (!hanwang || !input_dev) {
diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c
index d2ac7c2b5b82..2812f9236b7d 100644
--- a/drivers/input/tablet/kbtab.c
+++ b/drivers/input/tablet/kbtab.c
@@ -122,6 +122,9 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i
struct input_dev *input_dev;
int error = -ENOMEM;
+ if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL);
input_dev = input_allocate_device();
if (!kbtab || !input_dev)
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index 45b466e3bbe8..0146e2c74649 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -500,6 +500,9 @@ static int sur40_probe(struct usb_interface *interface,
if (iface_desc->desc.bInterfaceClass != 0xFF)
return -ENODEV;
+ if (iface_desc->desc.bNumEndpoints < 5)
+ return -ENODEV;
+
/* Use endpoint #4 (0x86). */
endpoint = &iface_desc->endpoint[4].desc;
if (endpoint->bEndpointAddress != TOUCH_ENDPOINT)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 51159711b1d8..7922608287d7 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -234,6 +234,7 @@
#define SCTLR_S1_ASIDPNE (1 << 12)
#define SCTLR_CFCFG (1 << 7)
+#define SCTLR_HUPCF (1 << 8)
#define SCTLR_CFIE (1 << 6)
#define SCTLR_CFRE (1 << 5)
#define SCTLR_E (1 << 4)
@@ -1628,6 +1629,11 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
/* SCTLR */
reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_EAE_SBOP;
+ if (smmu_domain->attributes & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)) {
+ reg &= ~SCTLR_CFCFG;
+ reg |= SCTLR_HUPCF;
+ }
+
if ((!(smmu_domain->attributes & (1 << DOMAIN_ATTR_S1_BYPASS)) &&
!(smmu_domain->attributes & (1 << DOMAIN_ATTR_EARLY_MAP))) ||
!stage1)
@@ -3138,7 +3144,11 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
& (1 << DOMAIN_ATTR_ENABLE_TTBR1));
ret = 0;
break;
-
+ case DOMAIN_ATTR_CB_STALL_DISABLE:
+ *((int *)data) = !!(smmu_domain->attributes
+ & (1 << DOMAIN_ATTR_CB_STALL_DISABLE));
+ ret = 0;
+ break;
default:
ret = -ENODEV;
break;
@@ -3288,6 +3298,49 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
1 << DOMAIN_ATTR_ENABLE_TTBR1;
ret = 0;
break;
+ case DOMAIN_ATTR_GEOMETRY: {
+ struct iommu_domain_geometry *geometry =
+ (struct iommu_domain_geometry *)data;
+
+ if (smmu_domain->smmu != NULL) {
+ dev_err(smmu_domain->smmu->dev,
+ "cannot set geometry attribute while attached\n");
+ ret = -EBUSY;
+ break;
+ }
+
+ if (geometry->aperture_start >= SZ_1G * 4ULL ||
+ geometry->aperture_end >= SZ_1G * 4ULL) {
+ pr_err("fastmap does not support IOVAs >= 4GB\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (smmu_domain->attributes
+ & (1 << DOMAIN_ATTR_GEOMETRY)) {
+ if (geometry->aperture_start
+ < domain->geometry.aperture_start)
+ domain->geometry.aperture_start =
+ geometry->aperture_start;
+
+ if (geometry->aperture_end
+ > domain->geometry.aperture_end)
+ domain->geometry.aperture_end =
+ geometry->aperture_end;
+ } else {
+ smmu_domain->attributes |= 1 << DOMAIN_ATTR_GEOMETRY;
+ domain->geometry.aperture_start =
+ geometry->aperture_start;
+ domain->geometry.aperture_end = geometry->aperture_end;
+ }
+ ret = 0;
+ break;
+ }
+ case DOMAIN_ATTR_CB_STALL_DISABLE:
+ if (*((int *)data))
+ smmu_domain->attributes |=
+ 1 << DOMAIN_ATTR_CB_STALL_DISABLE;
+ ret = 0;
+ break;
default:
ret = -ENODEV;
break;
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index 8c6364f03eac..0881d68f34d8 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -188,7 +188,9 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping,
iommu_tlbiall(mapping->domain);
mapping->have_stale_tlbs = false;
- av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, mapping->base,
+ av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start,
+ mapping->base,
mapping->base + mapping->size - 1,
skip_sync);
}
@@ -367,7 +369,8 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page,
if (unlikely(iova == DMA_ERROR_CODE))
goto fail;
- pmd = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, iova);
+ pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start, iova);
if (unlikely(av8l_fast_map_public(pmd, phys_to_map, len, prot)))
goto fail_free_iova;
@@ -391,7 +394,8 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova,
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
unsigned long flags;
av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
- mapping->base, iova);
+ mapping->domain->geometry.aperture_start,
+ iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
size_t len = ALIGN(size + offset, FAST_PAGE_SIZE);
int nptes = len >> FAST_PAGE_SHIFT;
@@ -414,7 +418,8 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev,
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
- mapping->base, iova);
+ mapping->domain->geometry.aperture_start,
+ iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
@@ -427,7 +432,8 @@ static void fast_smmu_sync_single_for_device(struct device *dev,
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
- mapping->base, iova);
+ mapping->domain->geometry.aperture_start,
+ iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
@@ -555,8 +561,9 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
while (sg_miter_next(&miter)) {
int nptes = miter.length >> FAST_PAGE_SHIFT;
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base,
- iova_iter);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start,
+ iova_iter);
if (unlikely(av8l_fast_map_public(
ptep, page_to_phys(miter.page),
miter.length, prot))) {
@@ -584,7 +591,9 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
out_unmap:
/* need to take the lock again for page tables and iova */
spin_lock_irqsave(&mapping->lock, flags);
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_addr);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start,
+ dma_addr);
av8l_fast_unmap_public(ptep, size);
fast_dmac_clean_range(mapping, ptep, ptep + count);
out_free_iova:
@@ -616,7 +625,8 @@ static void fast_smmu_free(struct device *dev, size_t size,
pages = area->pages;
dma_common_free_remap(vaddr, size, VM_USERMAP, false);
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_handle);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start, dma_handle);
spin_lock_irqsave(&mapping->lock, flags);
av8l_fast_unmap_public(ptep, size);
fast_dmac_clean_range(mapping, ptep, ptep + count);
@@ -720,7 +730,7 @@ static const struct dma_map_ops fast_smmu_dma_ops = {
*
* Creates a mapping structure which holds information about used/unused IO
* address ranges, which is required to perform mapping with IOMMU aware
- * functions. The only VA range supported is [0, 4GB).
+ * functions. The only VA range supported is [0, 4GB].
*
* The client device need to be attached to the mapping with
* fast_smmu_attach_device function.
@@ -774,6 +784,7 @@ int fast_smmu_attach_device(struct device *dev,
struct iommu_domain *domain = mapping->domain;
struct iommu_pgtbl_info info;
u64 size = (u64)mapping->bits << PAGE_SHIFT;
+ struct iommu_domain_geometry geometry;
if (mapping->base + size > (SZ_1G * 4ULL))
return -EINVAL;
@@ -788,8 +799,11 @@ int fast_smmu_attach_device(struct device *dev,
mapping->fast->domain = domain;
mapping->fast->dev = dev;
- domain->geometry.aperture_start = mapping->base;
- domain->geometry.aperture_end = mapping->base + size - 1;
+ geometry.aperture_start = mapping->base;
+ geometry.aperture_end = mapping->base + size - 1;
+ if (iommu_domain_set_attr(domain, DOMAIN_ATTR_GEOMETRY,
+ &geometry))
+ return -EINVAL;
if (iommu_attach_device(domain, dev))
return -EINVAL;
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index f0fc6f7b5d98..0628372f3591 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -908,7 +908,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
* which we used for the IOMMU lookup. Strictly speaking
* we could do this for all PCI devices; we only need to
* get the BDF# from the scope table for ACPI matches. */
- if (pdev->is_virtfn)
+ if (pdev && pdev->is_virtfn)
goto got_pdev;
*bus = drhd->devices[i].bus;
diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c
index 3582e206db68..5378e95c4627 100644
--- a/drivers/iommu/io-pgtable-fast.c
+++ b/drivers/iommu/io-pgtable-fast.c
@@ -133,6 +133,9 @@ struct av8l_fast_io_pgtable {
#define AV8L_FAST_TCR_EPD1_SHIFT 23
#define AV8L_FAST_TCR_EPD1_FAULT 1
+#define AV8L_FAST_TCR_SEP_SHIFT (15 + 32)
+#define AV8L_FAST_TCR_SEP_UPSTREAM 7ULL
+
#define AV8L_FAST_MAIR_ATTR_SHIFT(n) ((n) << 3)
#define AV8L_FAST_MAIR_ATTR_MASK 0xff
#define AV8L_FAST_MAIR_ATTR_DEVICE 0x04
@@ -173,12 +176,12 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep)
}
void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, u64 base,
- u64 end, bool skip_sync)
+ u64 start, u64 end, bool skip_sync)
{
int i;
- av8l_fast_iopte *pmdp = pmds;
+ av8l_fast_iopte *pmdp = iopte_pmd_offset(pmds, base, start);
- for (i = base >> AV8L_FAST_PAGE_SHIFT;
+ for (i = start >> AV8L_FAST_PAGE_SHIFT;
i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) {
if (!(*pmdp & AV8L_FAST_PTE_VALID)) {
*pmdp = 0;
@@ -256,16 +259,17 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size)
__av8l_fast_unmap(ptep, size, true);
}
-/* upper layer must take care of TLB invalidation */
static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova,
size_t size)
{
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
+ struct io_pgtable *iop = &data->iop;
av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT;
__av8l_fast_unmap(ptep, size, false);
dmac_clean_range(ptep, ptep + nptes);
+ iop->cfg.tlb->tlb_flush_all(iop->cookie);
return size;
}
@@ -522,6 +526,7 @@ av8l_fast_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
#if defined(CONFIG_ARM)
reg |= ARM_32_LPAE_TCR_EAE;
#endif
+ reg |= AV8L_FAST_TCR_SEP_UPSTREAM << AV8L_FAST_TCR_SEP_SHIFT;
cfg->av8l_fast_cfg.tcr = reg;
/* MAIRs */
@@ -668,7 +673,7 @@ static int __init av8l_fast_positive_testing(void)
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, base, max, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 8K map calls */
for (iova = base; iova < max; iova += SZ_8K) {
@@ -689,7 +694,7 @@ static int __init av8l_fast_positive_testing(void)
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, base, max, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 16K map calls */
for (iova = base; iova < max; iova += SZ_16K) {
@@ -710,7 +715,7 @@ static int __init av8l_fast_positive_testing(void)
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, base, max, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 64K map calls */
for (iova = base; iova < max; iova += SZ_64K) {
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 75fcde6e2c20..ffeb47c6b367 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -72,6 +72,8 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
return "DOMAIN_ATTR_FAST";
case DOMAIN_ATTR_EARLY_MAP:
return "DOMAIN_ATTR_EARLY_MAP";
+ case DOMAIN_ATTR_CB_STALL_DISABLE:
+ return "DOMAIN_ATTR_CB_STALL_DISABLE";
default:
return "Unknown attr!";
}
@@ -787,9 +789,13 @@ static int iommu_debug_profiling_fast_show(struct seq_file *s, void *ignored)
enum iommu_attr attrs[] = {
DOMAIN_ATTR_FAST,
DOMAIN_ATTR_ATOMIC,
+ DOMAIN_ATTR_GEOMETRY,
};
int one = 1;
- void *attr_values[] = { &one, &one, &one };
+ struct iommu_domain_geometry geometry = {0, 0, 0};
+ void *attr_values[] = { &one, &one, &geometry};
+
+ geometry.aperture_end = (dma_addr_t)(SZ_1G * 4ULL - 1);
iommu_debug_device_profiling(s, ddev->dev, attrs, attr_values,
ARRAY_SIZE(attrs), sizes);
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index aecec6d32463..7f1c625b08ec 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -2317,6 +2317,9 @@ static int gigaset_probe(struct usb_interface *interface,
return -ENODEV;
}
+ if (hostif->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
dev_info(&udev->dev,
"%s: Device matched (Vendor: 0x%x, Product: 0x%x)\n",
__func__, le16_to_cpu(udev->descriptor.idVendor),
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 4c28e0922c84..1a6cad946a25 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -127,11 +127,11 @@
#define FLASH_LED_LMH_MITIGATION_DISABLE 0
#define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4)
#define FLASH_LED_CHGR_MITIGATION_DISABLE 0
-#define FLASH_LED_MITIGATION_SEL_DEFAULT 2
+#define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2
#define FLASH_LED_MITIGATION_SEL_MAX 2
#define FLASH_LED_CHGR_MITIGATION_SEL_SHIFT 4
-#define FLASH_LED_MITIGATION_THRSH_DEFAULT 0xA
-#define FLASH_LED_MITIGATION_THRSH_MAX 0x1F
+#define FLASH_LED_CHGR_MITIGATION_THRSH_DEFAULT 0xA
+#define FLASH_LED_CHGR_MITIGATION_THRSH_MAX 0x1F
#define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000
#define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000
#define FLASH_LED_IRES_BASE 3
@@ -152,12 +152,17 @@
#define FLASH_LED_MOD_ENABLE BIT(7)
#define FLASH_LED_DISABLE 0x00
#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
-#define FLASH_LED_MIN_CURRENT_MA 25
#define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750
/* notifier call chain for flash-led irqs */
static ATOMIC_NOTIFIER_HEAD(irq_notifier_list);
+enum flash_charger_mitigation {
+ FLASH_DISABLE_CHARGER_MITIGATION,
+ FLASH_HW_CHARGER_MITIGATION_BY_ILED_THRSHLD,
+ FLASH_SW_CHARGER_MITIGATION,
+};
+
enum flash_led_type {
FLASH_LED_TYPE_FLASH,
FLASH_LED_TYPE_TORCH,
@@ -182,6 +187,7 @@ struct flash_node_data {
int ires_ua;
int max_current;
int current_ma;
+ int prev_current_ma;
u8 duration;
u8 id;
u8 type;
@@ -261,6 +267,7 @@ struct qpnp_flash_led {
int num_fnodes;
int num_snodes;
int enable;
+ int total_current_ma;
u16 base;
bool trigger_lmh;
bool trigger_chgr;
@@ -487,10 +494,12 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
if (rc < 0)
return rc;
+ val = led->pdata->chgr_mitigation_sel
+ << FLASH_LED_CHGR_MITIGATION_SEL_SHIFT;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_MITIGATION_SEL(led->base),
FLASH_LED_CHGR_MITIGATION_SEL_MASK,
- led->pdata->chgr_mitigation_sel);
+ val);
if (rc < 0)
return rc;
@@ -876,14 +885,29 @@ static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led)
return max_avail_current;
}
+static void qpnp_flash_led_aggregate_max_current(struct flash_node_data *fnode)
+{
+ struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev);
+
+ if (fnode->current_ma)
+ led->total_current_ma += fnode->current_ma
+ - fnode->prev_current_ma;
+ else
+ led->total_current_ma -= fnode->prev_current_ma;
+
+ fnode->prev_current_ma = fnode->current_ma;
+}
+
static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
{
int prgm_current_ma = value;
+ int min_ma = fnode->ires_ua / 1000;
+ struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev);
if (value <= 0)
prgm_current_ma = 0;
- else if (value < FLASH_LED_MIN_CURRENT_MA)
- prgm_current_ma = FLASH_LED_MIN_CURRENT_MA;
+ else if (value < min_ma)
+ prgm_current_ma = min_ma;
prgm_current_ma = min(prgm_current_ma, fnode->max_current);
fnode->current_ma = prgm_current_ma;
@@ -891,6 +915,13 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma,
fnode->ires_ua);
fnode->led_on = prgm_current_ma != 0;
+
+ if (led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) {
+ qpnp_flash_led_aggregate_max_current(fnode);
+ led->trigger_chgr = false;
+ if (led->total_current_ma >= 1000)
+ led->trigger_chgr = true;
+ }
}
static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
@@ -1158,10 +1189,6 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
*max_current = rc;
}
- led->trigger_chgr = false;
- if (options & PRE_FLASH)
- led->trigger_chgr = true;
-
return 0;
}
@@ -1335,7 +1362,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
struct flash_node_data *fnode, struct device_node *node)
{
const char *temp_string;
- int rc;
+ int rc, min_ma;
u32 val;
bool strobe_sel = 0, edge_trigger = 0, active_high = 0;
@@ -1391,10 +1418,11 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
return rc;
}
+ min_ma = fnode->ires_ua / 1000;
rc = of_property_read_u32(node, "qcom,max-current", &val);
if (!rc) {
- if (val < FLASH_LED_MIN_CURRENT_MA)
- val = FLASH_LED_MIN_CURRENT_MA;
+ if (val < min_ma)
+ val = min_ma;
fnode->max_current = val;
fnode->cdev.max_brightness = val;
} else {
@@ -1404,11 +1432,10 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
rc = of_property_read_u32(node, "qcom,current-ma", &val);
if (!rc) {
- if (val < FLASH_LED_MIN_CURRENT_MA ||
- val > fnode->max_current)
+ if (val < min_ma || val > fnode->max_current)
pr_warn("Invalid operational current specified, capping it\n");
- if (val < FLASH_LED_MIN_CURRENT_MA)
- val = FLASH_LED_MIN_CURRENT_MA;
+ if (val < min_ma)
+ val = min_ma;
if (val > fnode->max_current)
val = fnode->max_current;
fnode->current_ma = val;
@@ -1956,7 +1983,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
- led->pdata->lmh_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT;
+ led->pdata->lmh_mitigation_sel = FLASH_LED_LMH_MITIGATION_SEL_DEFAULT;
rc = of_property_read_u32(node, "qcom,lmh-mitigation-sel", &val);
if (!rc) {
led->pdata->lmh_mitigation_sel = val;
@@ -1970,7 +1997,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return -EINVAL;
}
- led->pdata->chgr_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT;
+ led->pdata->chgr_mitigation_sel = FLASH_SW_CHARGER_MITIGATION;
rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val);
if (!rc) {
led->pdata->chgr_mitigation_sel = val;
@@ -1984,9 +2011,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return -EINVAL;
}
- led->pdata->chgr_mitigation_sel <<= FLASH_LED_CHGR_MITIGATION_SEL_SHIFT;
-
- led->pdata->iled_thrsh_val = FLASH_LED_MITIGATION_THRSH_DEFAULT;
+ led->pdata->iled_thrsh_val = FLASH_LED_CHGR_MITIGATION_THRSH_DEFAULT;
rc = of_property_read_u32(node, "qcom,iled-thrsh-ma", &val);
if (!rc) {
led->pdata->iled_thrsh_val = MITIGATION_THRSH_MA_TO_VAL(val);
@@ -1995,7 +2020,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
- if (led->pdata->iled_thrsh_val > FLASH_LED_MITIGATION_THRSH_MAX) {
+ if (led->pdata->iled_thrsh_val > FLASH_LED_CHGR_MITIGATION_THRSH_MAX) {
pr_err("Invalid iled_thrsh_val specified\n");
return -EINVAL;
}
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 36ca4e4cbfb7..4c15dee0857b 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -532,16 +532,27 @@ config DM_LOG_WRITES
If unsure, say N.
-config DM_ANDROID_VERITY
- tristate "Android verity target support"
+config DM_VERITY_AVB
+ tristate "Support AVB specific verity error behavior"
depends on DM_VERITY
+ ---help---
+ Enables Android Verified Boot platform-specific error
+ behavior. In particular, it will modify the vbmeta partition
+ specified on the kernel command-line when non-transient error
+ occurs (followed by a panic).
+
+ If unsure, say N.
+
+config DM_ANDROID_VERITY
+ bool "Android verity target support"
+ depends on DM_VERITY=y
depends on X509_CERTIFICATE_PARSER
depends on SYSTEM_TRUSTED_KEYRING
depends on PUBLIC_KEY_ALGO_RSA
depends on KEYS
depends on ASYMMETRIC_KEY_TYPE
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
- depends on MD_LINEAR
+ depends on MD_LINEAR=y
select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
---help---
This device-mapper target is virtually a VERITY target. This
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 2b2ba36638cd..eef9097c60b3 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -74,3 +74,7 @@ endif
ifeq ($(CONFIG_DM_ANDROID_VERITY),y)
dm-verity-objs += dm-android-verity.o
endif
+
+ifeq ($(CONFIG_DM_VERITY_AVB),y)
+dm-verity-objs += dm-verity-avb.o
+endif
diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c
index 13c60bee8af5..7cef735a01a7 100644
--- a/drivers/md/dm-android-verity.c
+++ b/drivers/md/dm-android-verity.c
@@ -115,6 +115,12 @@ static inline bool is_userdebug(void)
return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug));
}
+static inline bool is_unlocked(void)
+{
+ static const char unlocked[] = "orange";
+
+ return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked));
+}
static int table_extract_mpi_array(struct public_key_signature *pks,
const void *data, size_t len)
@@ -585,6 +591,8 @@ static int verify_verity_signature(char *key_id,
if (IS_ERR(pks)) {
DMERR("hashing failed");
+ retval = PTR_ERR(pks);
+ pks = NULL;
goto error;
}
@@ -648,6 +656,28 @@ static int add_as_linear_device(struct dm_target *ti, char *dev)
return err;
}
+static int create_linear_device(struct dm_target *ti, dev_t dev,
+ char *target_device)
+{
+ u64 device_size = 0;
+ int err = find_size(dev, &device_size);
+
+ if (err) {
+ DMERR("error finding bdev size");
+ handle_error();
+ return err;
+ }
+
+ ti->len = device_size;
+ err = add_as_linear_device(ti, target_device);
+ if (err) {
+ handle_error();
+ return err;
+ }
+ verity_enabled = false;
+ return 0;
+}
+
/*
* Target parameters:
* <key id> Key id of the public key in the system keyring.
@@ -671,7 +701,6 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
struct fec_ecc_metadata uninitialized_var(ecc);
char buf[FEC_ARG_LENGTH], *buf_ptr;
unsigned long long tmpll;
- u64 uninitialized_var(device_size);
if (argc == 1) {
/* Use the default keyid */
@@ -699,23 +728,8 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
return -EINVAL;
}
- if (is_eng()) {
- err = find_size(dev, &device_size);
- if (err) {
- DMERR("error finding bdev size");
- handle_error();
- return err;
- }
-
- ti->len = device_size;
- err = add_as_linear_device(ti, target_device);
- if (err) {
- handle_error();
- return err;
- }
- verity_enabled = false;
- return 0;
- }
+ if (is_eng())
+ return create_linear_device(ti, dev, target_device);
strreplace(key_id, '#', ' ');
@@ -730,6 +744,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
err = extract_metadata(dev, &fec, &metadata, &verity_enabled);
if (err) {
+ /* Allow invalid metadata when the device is unlocked */
+ if (is_unlocked()) {
+ DMWARN("Allow invalid metadata when unlocked");
+ return create_linear_device(ti, dev, target_device);
+ }
DMERR("Error while extracting metadata");
handle_error();
goto free_metadata;
diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c
new file mode 100644
index 000000000000..88487346c4c6
--- /dev/null
+++ b/drivers/md/dm-verity-avb.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 Google.
+ *
+ * This file is released under the GPLv2.
+ *
+ * Based on drivers/md/dm-verity-chromeos.c
+ */
+
+#include <linux/device-mapper.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+
+#define DM_MSG_PREFIX "verity-avb"
+
+/* Set via module parameter. */
+static char avb_vbmeta_device[64];
+
+static void invalidate_vbmeta_endio(struct bio *bio)
+{
+ complete(bio->bi_private);
+}
+
+static int invalidate_vbmeta_submit(struct bio *bio,
+ struct block_device *bdev,
+ int rw, int access_last_sector,
+ struct page *page)
+{
+ DECLARE_COMPLETION_ONSTACK(wait);
+
+ bio->bi_private = &wait;
+ bio->bi_end_io = invalidate_vbmeta_endio;
+ bio->bi_bdev = bdev;
+
+ bio->bi_iter.bi_sector = 0;
+ if (access_last_sector) {
+ sector_t last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1;
+ bio->bi_iter.bi_sector = last_sector;
+ }
+ bio->bi_vcnt = 1;
+ bio->bi_iter.bi_idx = 0;
+ bio->bi_iter.bi_size = 512;
+ bio->bi_iter.bi_bvec_done = 0;
+ bio->bi_rw = rw;
+ bio->bi_io_vec[0].bv_page = page;
+ bio->bi_io_vec[0].bv_len = 512;
+ bio->bi_io_vec[0].bv_offset = 0;
+
+ submit_bio(rw, bio);
+ /* Wait up to 2 seconds for completion or fail. */
+ if (!wait_for_completion_timeout(&wait, msecs_to_jiffies(2000)))
+ return -EIO;
+ return 0;
+}
+
+static int invalidate_vbmeta(dev_t vbmeta_devt)
+{
+ int ret = 0;
+ struct block_device *bdev;
+ struct bio *bio;
+ struct page *page;
+ fmode_t dev_mode;
+ /* Ensure we do synchronous unblocked I/O. We may also need
+ * sync_bdev() on completion, but it really shouldn't.
+ */
+ int rw = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE;
+ int access_last_sector = 0;
+
+ /* First we open the device for reading. */
+ dev_mode = FMODE_READ | FMODE_EXCL;
+ bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode,
+ invalidate_vbmeta);
+ if (IS_ERR(bdev)) {
+ DMERR("invalidate_kernel: could not open device for reading");
+ dev_mode = 0;
+ ret = -ENOENT;
+ goto failed_to_read;
+ }
+
+ bio = bio_alloc(GFP_NOIO, 1);
+ if (!bio) {
+ ret = -ENOMEM;
+ goto failed_bio_alloc;
+ }
+
+ page = alloc_page(GFP_NOIO);
+ if (!page) {
+ ret = -ENOMEM;
+ goto failed_to_alloc_page;
+ }
+
+ access_last_sector = 0;
+ ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page);
+ if (ret) {
+ DMERR("invalidate_vbmeta: error reading");
+ goto failed_to_submit_read;
+ }
+
+ /* We have a page. Let's make sure it looks right. */
+ if (memcmp("AVB0", page_address(page), 4) == 0) {
+ /* Stamp it. */
+ memcpy(page_address(page), "AVE0", 4);
+ DMINFO("invalidate_vbmeta: found vbmeta partition");
+ } else {
+ /* Could be this is on a AVB footer, check. Also, since the
+ * AVB footer is in the last 64 bytes, adjust for the fact that
+ * we're dealing with 512-byte sectors.
+ */
+ size_t offset = (1<<SECTOR_SHIFT) - 64;
+
+ access_last_sector = 1;
+ ret = invalidate_vbmeta_submit(bio, bdev, rw,
+ access_last_sector, page);
+ if (ret) {
+ DMERR("invalidate_vbmeta: error reading");
+ goto failed_to_submit_read;
+ }
+ if (memcmp("AVBf", page_address(page) + offset, 4) != 0) {
+ DMERR("invalidate_vbmeta called on non-vbmeta partition");
+ ret = -EINVAL;
+ goto invalid_header;
+ }
+ /* Stamp it. */
+ memcpy(page_address(page) + offset, "AVE0", 4);
+ DMINFO("invalidate_vbmeta: found vbmeta footer partition");
+ }
+
+ /* Now rewrite the changed page - the block dev was being
+ * changed on read. Let's reopen here.
+ */
+ blkdev_put(bdev, dev_mode);
+ dev_mode = FMODE_WRITE | FMODE_EXCL;
+ bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode,
+ invalidate_vbmeta);
+ if (IS_ERR(bdev)) {
+ DMERR("invalidate_vbmeta: could not open device for writing");
+ dev_mode = 0;
+ ret = -ENOENT;
+ goto failed_to_write;
+ }
+
+ /* We re-use the same bio to do the write after the read. Need to reset
+ * it to initialize bio->bi_remaining.
+ */
+ bio_reset(bio);
+
+ rw |= REQ_WRITE;
+ ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page);
+ if (ret) {
+ DMERR("invalidate_vbmeta: error writing");
+ goto failed_to_submit_write;
+ }
+
+ DMERR("invalidate_vbmeta: completed.");
+ ret = 0;
+failed_to_submit_write:
+failed_to_write:
+invalid_header:
+ __free_page(page);
+failed_to_submit_read:
+ /* Technically, we'll leak a page with the pending bio, but
+ * we're about to reboot anyway.
+ */
+failed_to_alloc_page:
+ bio_put(bio);
+failed_bio_alloc:
+ if (dev_mode)
+ blkdev_put(bdev, dev_mode);
+failed_to_read:
+ return ret;
+}
+
+void dm_verity_avb_error_handler(void)
+{
+ dev_t dev;
+
+ DMINFO("AVB error handler called for %s", avb_vbmeta_device);
+
+ if (avb_vbmeta_device[0] == '\0') {
+ DMERR("avb_vbmeta_device parameter not set");
+ goto fail_no_dev;
+ }
+
+ dev = name_to_dev_t(avb_vbmeta_device);
+ if (!dev) {
+ DMERR("No matching partition for device: %s",
+ avb_vbmeta_device);
+ goto fail_no_dev;
+ }
+
+ invalidate_vbmeta(dev);
+
+fail_no_dev:
+ ;
+}
+
+static int __init dm_verity_avb_init(void)
+{
+ DMINFO("AVB error handler initialized with vbmeta device: %s",
+ avb_vbmeta_device);
+ return 0;
+}
+
+static void __exit dm_verity_avb_exit(void)
+{
+}
+
+module_init(dm_verity_avb_init);
+module_exit(dm_verity_avb_exit);
+
+MODULE_AUTHOR("David Zeuthen <zeuthen@google.com>");
+MODULE_DESCRIPTION("AVB-specific error handler for dm-verity");
+MODULE_LICENSE("GPL");
+
+/* Declare parameter with no module prefix */
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "androidboot.vbmeta."
+module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0);
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index c7e97cf6e7fb..e34cf53bd068 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -233,8 +233,12 @@ out:
if (v->mode == DM_VERITY_MODE_LOGGING)
return 0;
- if (v->mode == DM_VERITY_MODE_RESTART)
+ if (v->mode == DM_VERITY_MODE_RESTART) {
+#ifdef CONFIG_DM_VERITY_AVB
+ dm_verity_avb_error_handler();
+#endif
kernel_restart("dm-verity device corrupted");
+ }
return 1;
}
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
index 75effca400a3..a90d1d416107 100644
--- a/drivers/md/dm-verity.h
+++ b/drivers/md/dm-verity.h
@@ -136,4 +136,5 @@ extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits);
extern void verity_dtr(struct dm_target *ti);
extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
extern int verity_map(struct dm_target *ti, struct bio *bio);
+extern void dm_verity_avb_error_handler(void);
#endif /* DM_VERITY_H */
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index ebb0dd612ebd..a92979e704e3 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1072,6 +1072,8 @@ static void __make_request(struct mddev *mddev, struct bio *bio)
int max_sectors;
int sectors;
+ md_write_start(mddev, bio);
+
/*
* Register the new request and wait if the reconstruction
* thread has put up a bar for new requests.
@@ -1455,8 +1457,6 @@ static void make_request(struct mddev *mddev, struct bio *bio)
return;
}
- md_write_start(mddev, bio);
-
do {
/*
@@ -1477,7 +1477,25 @@ static void make_request(struct mddev *mddev, struct bio *bio)
split = bio;
}
+ /*
+ * If a bio is splitted, the first part of bio will pass
+ * barrier but the bio is queued in current->bio_list (see
+ * generic_make_request). If there is a raise_barrier() called
+ * here, the second part of bio can't pass barrier. But since
+ * the first part bio isn't dispatched to underlaying disks
+ * yet, the barrier is never released, hence raise_barrier will
+ * alays wait. We have a deadlock.
+ * Note, this only happens in read path. For write path, the
+ * first part of bio is dispatched in a schedule() call
+ * (because of blk plug) or offloaded to raid10d.
+ * Quitting from the function immediately can change the bio
+ * order queued in bio_list and avoid the deadlock.
+ */
__make_request(mddev, split);
+ if (split != bio && bio_data_dir(bio) == READ) {
+ generic_make_request(bio);
+ break;
+ }
} while (split != bio);
/* In case raid10d snuck in to freeze_array */
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 139a4c9b49ee..b283f6277b87 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -497,6 +497,7 @@ struct msm_vfe_src_info {
enum msm_vfe_dual_hw_type dual_hw_type;
struct msm_vfe_dual_hw_ms_info dual_hw_ms_info;
bool accept_frame;
+ uint32_t lpm;
};
struct msm_vfe_fetch_engine_info {
@@ -599,7 +600,7 @@ struct msm_vfe_tasklet_queue_cmd {
struct vfe_device *vfe_dev;
};
-#define MSM_VFE_TASKLETQ_SIZE 200
+#define MSM_VFE_TASKLETQ_SIZE 400
enum msm_vfe_overflow_state {
NO_OVERFLOW,
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 ebd3a32281d7..63e46125c292 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
@@ -773,6 +773,40 @@ void msm_isp_check_for_output_error(struct vfe_device *vfe_dev,
}
}
+static int msm_isp_check_sync_time(struct msm_vfe_src_info *src_info,
+ struct msm_isp_timestamp *ts,
+ struct master_slave_resource_info *ms_res)
+{
+ int i;
+ struct msm_vfe_src_info *master_src_info = NULL;
+ uint32_t master_time = 0, current_time;
+
+ if (!ms_res->src_sof_mask)
+ return 0;
+
+ for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) {
+ if (ms_res->src_info[i] == NULL)
+ continue;
+ if (src_info == ms_res->src_info[i] ||
+ ms_res->src_info[i]->active == 0)
+ continue;
+ if (ms_res->src_sof_mask &
+ (1 << ms_res->src_info[i]->dual_hw_ms_info.index)) {
+ master_src_info = ms_res->src_info[i];
+ break;
+ }
+ }
+ if (!master_src_info)
+ return 0;
+ master_time = master_src_info->
+ dual_hw_ms_info.sof_info.mono_timestamp_ms;
+ current_time = ts->buf_time.tv_sec * 1000 +
+ ts->buf_time.tv_usec / 1000;
+ if ((current_time - master_time) > ms_res->sof_delta_threshold)
+ return 1;
+ return 0;
+}
+
static void msm_isp_sync_dual_cam_frame_id(
struct vfe_device *vfe_dev,
struct master_slave_resource_info *ms_res,
@@ -787,11 +821,24 @@ static void msm_isp_sync_dual_cam_frame_id(
if (src_info->dual_hw_ms_info.sync_state ==
ms_res->dual_sync_mode) {
- (frame_src == VFE_PIX_0) ? src_info->frame_id +=
+ if (msm_isp_check_sync_time(src_info, ts, ms_res) == 0) {
+ (frame_src == VFE_PIX_0) ? src_info->frame_id +=
vfe_dev->axi_data.src_info[frame_src].
sof_counter_step :
src_info->frame_id++;
- return;
+ return;
+ }
+ ms_res->src_sof_mask = 0;
+ ms_res->active_src_mask = 0;
+ for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) {
+ if (ms_res->src_info[i] == NULL)
+ continue;
+ if (ms_res->src_info[i]->active == 0)
+ continue;
+ ms_res->src_info[i]->dual_hw_ms_info.
+ sync_state =
+ MSM_ISP_DUAL_CAM_ASYNC;
+ }
}
WARN_ON(ms_res->dual_sync_mode == MSM_ISP_DUAL_CAM_ASYNC);
@@ -948,8 +995,6 @@ static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev,
uint32_t pingpong_status = 0, pingpong_bit = 0;
struct msm_isp_buffer *done_buf = NULL;
int vfe_idx = -1;
- /* initialize pd_buf_idx with an invalid index 0xF */
- vfe_dev->pd_buf_idx = 0xF;
if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2)
return;
@@ -2421,6 +2466,7 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
int i, rc = 0;
uint64_t total_bandwidth = 0;
int vfe_idx;
+ uint32_t intf;
unsigned long flags;
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_dual_lpm_mode *ab_ib_vote = NULL;
@@ -2436,7 +2482,13 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
stream_info =
msm_isp_get_stream_common_data(vfe_dev,
ab_ib_vote->stream_src[i]);
+ if (stream_info == NULL)
+ continue;
+ /* loop all stream on current session */
spin_lock_irqsave(&stream_info->lock, flags);
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ vfe_dev->axi_data.src_info[intf].lpm =
+ ab_ib_vote->lpm_mode;
if (stream_info->state == ACTIVE) {
vfe_idx =
msm_isp_get_vfe_idx_for_stream(vfe_dev,
@@ -2457,7 +2509,12 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
stream_info =
msm_isp_get_stream_common_data(vfe_dev,
ab_ib_vote->stream_src[i]);
+ if (stream_info == NULL)
+ continue;
spin_lock_irqsave(&stream_info->lock, flags);
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ vfe_dev->axi_data.src_info[intf].lpm =
+ ab_ib_vote->lpm_mode;
if (stream_info->state == PAUSED) {
vfe_idx =
msm_isp_get_vfe_idx_for_stream(vfe_dev,
@@ -2883,7 +2940,9 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
* those state transitions instead of directly forcing stream to
* be INACTIVE
*/
- if (stream_info->state != PAUSED) {
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ if ((!vfe_dev->axi_data.src_info[intf].lpm) ||
+ stream_info->state != PAUSED) {
while (stream_info->state != ACTIVE)
__msm_isp_axi_stream_update(stream_info,
&timestamp);
@@ -2900,10 +2959,12 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.axi_ops.
clear_wm_irq_mask(vfe_dev, stream_info);
}
- if (stream_info->state == ACTIVE) {
+ if (stream_info->state == ACTIVE &&
+ !vfe_dev->axi_data.src_info[intf].lpm) {
init_completion(&stream_info->inactive_comp);
stream_info->state = STOP_PENDING;
- } else if (stream_info->state == PAUSED) {
+ } else if (vfe_dev->axi_data.src_info[intf].lpm ||
+ stream_info->state == PAUSED) {
/* don't wait for reg update */
stream_info->state = STOP_PENDING;
msm_isp_axi_stream_enable_cfg(stream_info);
@@ -3018,6 +3079,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl,
int k;
struct vfe_device *vfe_dev;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev_ioctl->axi_data;
+ uint32_t intf;
if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
return -EINVAL;
@@ -3081,7 +3143,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl,
cfg_wm_irq_mask(vfe_dev, stream_info);
}
}
-
+ intf = SRC_TO_INTF(stream_info->stream_src);
init_completion(&stream_info->active_comp);
stream_info->state = START_PENDING;
msm_isp_update_intf_stream_cnt(stream_info, 1);
@@ -3091,6 +3153,11 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl,
vfe_dev_ioctl->pdev->id);
if (src_state) {
src_mask |= (1 << SRC_TO_INTF(stream_info->stream_src));
+ if (vfe_dev_ioctl->axi_data.src_info[intf].lpm) {
+ while (stream_info->state != ACTIVE)
+ __msm_isp_axi_stream_update(
+ stream_info, &timestamp);
+ }
} else {
for (k = 0; k < stream_info->num_isp; k++) {
vfe_dev = stream_info->vfe_dev[k];
@@ -3365,22 +3432,21 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
/*
* If frame_id = 1 then no eof check is needed
*/
- if (vfe_dev->axi_data.src_info[VFE_PIX_0].active &&
- vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame == false) {
+ if (vfe_dev->axi_data.src_info[frame_src].active &&
+ frame_src == VFE_PIX_0 &&
+ vfe_dev->axi_data.src_info[frame_src].accept_frame == false) {
pr_debug("%s:%d invalid time to request frame %d\n",
__func__, __LINE__, frame_id);
goto error;
}
- if ((vfe_dev->axi_data.src_info[VFE_PIX_0].active && (frame_id !=
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + vfe_dev->
- axi_data.src_info[VFE_PIX_0].sof_counter_step)) ||
- ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id !=
+ if ((vfe_dev->axi_data.src_info[frame_src].active && (frame_id !=
vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev->
- axi_data.src_info[frame_src].sof_counter_step))) {
+ axi_data.src_info[VFE_PIX_0].sof_counter_step)) ||
+ ((!vfe_dev->axi_data.src_info[frame_src].active))) {
pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n",
__func__, __LINE__, frame_id,
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id,
- vfe_dev->axi_data.src_info[VFE_PIX_0].active);
+ vfe_dev->axi_data.src_info[frame_src].frame_id,
+ vfe_dev->axi_data.src_info[frame_src].active);
goto error;
}
if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) {
@@ -3887,6 +3953,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
&update_cmd->req_frm_ver2;
stream_info = msm_isp_get_stream_common_data(vfe_dev,
HANDLE_TO_IDX(req_frm->stream_handle));
+ if (stream_info == NULL) {
+ pr_err_ratelimited("%s: stream_info is NULL\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
rc = msm_isp_request_frame(vfe_dev, stream_info,
req_frm->user_stream_id,
req_frm->frame_id,
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 65009cb22286..a8d4cfb43927 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
@@ -141,6 +141,11 @@ static inline struct msm_vfe_axi_stream *msm_isp_get_stream_common_data(
struct msm_vfe_common_dev_data *common_data = vfe_dev->common_data;
struct msm_vfe_axi_stream *stream_info;
+ if (stream_idx >= VFE_AXI_SRC_MAX) {
+ pr_err("invalid stream_idx %d\n", stream_idx);
+ return NULL;
+ }
+
if (vfe_dev->is_split && stream_idx < RDI_INTF_0)
stream_info = &common_data->streams[stream_idx];
else
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 f2cf4d477b3f..73068a615380 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
@@ -228,9 +228,10 @@ static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev,
done_buf->buf_idx;
stats_event->pd_stats_idx = 0xF;
- if (stream_info->stats_type == MSM_ISP_STATS_BF)
+ if (stream_info->stats_type == MSM_ISP_STATS_BF) {
stats_event->pd_stats_idx = vfe_dev->pd_buf_idx;
-
+ vfe_dev->pd_buf_idx = 0xF;
+ }
if (comp_stats_type_mask == NULL) {
stats_event->stats_mask =
1 << stream_info->stats_type;
@@ -765,7 +766,7 @@ void msm_isp_process_stats_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
spin_unlock_irqrestore(&stream_info->lock, flags);
if (-EFAULT == rc) {
msm_isp_halt_send_error(vfe_dev,
- ISP_EVENT_BUF_FATAL_ERROR);
+ ISP_EVENT_PING_PONG_MISMATCH);
return;
}
continue;
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 765bf6521759..2a9bb6e8e505 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
@@ -2070,6 +2070,7 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev,
if (queue_cmd->cmd_used) {
pr_err("%s: Tasklet queue overflow: %d\n",
__func__, vfe_dev->pdev->id);
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
return;
} else {
atomic_add(1, &vfe_dev->irq_cnt);
@@ -2314,6 +2315,9 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
vfe_dev->reg_update_requested = 0;
/* Register page fault handler */
vfe_dev->buf_mgr->pagefault_debug_disable = 0;
+ /* initialize pd_buf_idx with an invalid index 0xF */
+ vfe_dev->pd_buf_idx = 0xF;
+
cam_smmu_reg_client_page_fault_handler(
vfe_dev->buf_mgr->iommu_hdl,
msm_vfe_iommu_fault_handler, vfe_dev);
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index 41d8ef577a27..caf6639f5151 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -49,17 +49,31 @@
#define ISPIF_TIMEOUT_ALL_US 1000000
#define ISPIF_SOF_DEBUG_COUNT 5
+/* 3D Threshold value according guidelines for line width 1280 */
+#define STEREO_DEFAULT_3D_THRESHOLD 0x36
+
+/*
+ * Overflows before restarting interface during stereo usecase
+ * to give some tolerance for cases when the two sensors sync fails
+ * this value is chosen by experiment
+ */
+#define MAX_PIX_OVERFLOW_ERROR_COUNT 10
+static int pix_overflow_error_count[VFE_MAX] = { 0 };
+
#undef CDBG
#ifdef CONFIG_MSMB_CAMERA_DEBUG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
#else
-#define CDBG(fmt, args...) do { } while (0)
+#define CDBG(fmt, args...)
#endif
static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable);
static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd,
unsigned int cmd, void *arg);
+static long msm_ispif_dispatch_cmd(enum ispif_cfg_type_t cmd,
+ struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params);
int msm_ispif_get_clk_info(struct ispif_device *ispif_dev,
struct platform_device *pdev);
@@ -249,16 +263,7 @@ static long msm_ispif_cmd_ext(struct v4l2_subdev *sd,
}
mutex_lock(&ispif->mutex);
- switch (pcdata.cfg_type) {
- case ISPIF_CFG2:
- rc = msm_ispif_config2(ispif, params);
- msm_ispif_io_dump_reg(ispif);
- break;
- default:
- pr_err("%s: invalid cfg_type\n", __func__);
- rc = -EINVAL;
- break;
- }
+ rc = msm_ispif_dispatch_cmd(pcdata.cfg_type, ispif, params);
mutex_unlock(&ispif->mutex);
kfree(params);
return rc;
@@ -855,15 +860,34 @@ static uint16_t msm_ispif_get_cids_mask_from_cfg(
return cids_mask;
}
+
+static uint16_t msm_ispif_get_right_cids_mask_from_cfg(
+ struct msm_ispif_right_param_entry *entry, int num_cids)
+{
+ int i;
+ uint16_t cids_mask = 0;
+
+ if (WARN_ON(!entry))
+ return cids_mask;
+
+ for (i = 0; i < num_cids && i < MAX_CID_CH_PARAM_ENTRY; i++) {
+ if (entry->cids[i] < CID_MAX)
+ cids_mask |= (1 << entry->cids[i]);
+ }
+
+ return cids_mask;
+}
+
static int msm_ispif_config(struct ispif_device *ispif,
void *data)
{
int rc = 0, i = 0;
- uint16_t cid_mask;
+ uint16_t cid_mask = 0;
+ uint16_t cid_right_mask = 0;
enum msm_ispif_intftype intftype;
enum msm_ispif_vfe_intf vfe_intf;
- struct msm_ispif_param_data *params =
- (struct msm_ispif_param_data *)data;
+ struct msm_ispif_param_data_ext *params =
+ (struct msm_ispif_param_data_ext *)data;
BUG_ON(!ispif);
BUG_ON(!params);
@@ -913,9 +937,15 @@ static int msm_ispif_config(struct ispif_device *ispif,
return -EINVAL;
}
- if (ispif->csid_version >= CSID_VERSION_V30)
+ if (ispif->csid_version >= CSID_VERSION_V30) {
msm_ispif_select_clk_mux(ispif, intftype,
params->entries[i].csid, vfe_intf);
+ if (intftype == PIX0 && params->stereo_enable &&
+ params->right_entries[i].csid < CSID_MAX)
+ msm_ispif_select_clk_mux(ispif, PIX1,
+ params->right_entries[i].csid,
+ vfe_intf);
+ }
rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf);
if (rc) {
@@ -926,10 +956,26 @@ static int msm_ispif_config(struct ispif_device *ispif,
msm_ispif_sel_csid_core(ispif, intftype,
params->entries[i].csid, vfe_intf);
+ if (intftype == PIX0 && params->stereo_enable &&
+ params->right_entries[i].csid < CSID_MAX)
+ /* configure right stereo csid */
+ msm_ispif_sel_csid_core(ispif, PIX1,
+ params->right_entries[i].csid, vfe_intf);
+
cid_mask = msm_ispif_get_cids_mask_from_cfg(
&params->entries[i]);
msm_ispif_enable_intf_cids(ispif, intftype,
cid_mask, vfe_intf, 1);
+ if (params->stereo_enable)
+ cid_right_mask = msm_ispif_get_right_cids_mask_from_cfg(
+ &params->right_entries[i],
+ params->entries[i].num_cids);
+ else
+ cid_right_mask = 0;
+ if (cid_right_mask && params->stereo_enable)
+ /* configure right stereo cids */
+ msm_ispif_enable_intf_cids(ispif, PIX1,
+ cid_right_mask, vfe_intf, 1);
if (params->entries[i].crop_enable)
msm_ispif_enable_crop(ispif, intftype, vfe_intf,
params->entries[i].crop_start_pixel,
@@ -962,8 +1008,28 @@ static int msm_ispif_config(struct ispif_device *ispif,
return rc;
}
+static void msm_ispif_config_stereo(struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params) {
+
+ int i;
+ enum msm_ispif_vfe_intf vfe_intf;
+
+ for (i = 0; i < params->num; i++) {
+ if (params->entries[i].intftype == PIX0 &&
+ params->stereo_enable &&
+ params->right_entries[i].csid < CSID_MAX) {
+ vfe_intf = params->entries[i].vfe_intf;
+ msm_camera_io_w_mb(0x3,
+ ispif->base + ISPIF_VFE_m_OUTPUT_SEL(vfe_intf));
+ msm_camera_io_w_mb(STEREO_DEFAULT_3D_THRESHOLD,
+ ispif->base +
+ ISPIF_VFE_m_3D_THRESHOLD(vfe_intf));
+ }
+ }
+}
+
static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits,
- struct msm_ispif_param_data *params)
+ struct msm_ispif_param_data_ext *params)
{
uint8_t vc;
int i, k;
@@ -1008,6 +1074,19 @@ static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits,
ispif->applied_intf_cmd[vfe_intf].intf_cmd |=
(cmd_bits << (vc * 2 + intf_type * 8));
}
+ if (intf_type == PIX0 && params->stereo_enable &&
+ params->right_entries[i].cids[k] < CID_MAX) {
+ cid = params->right_entries[i].cids[k];
+ vc = cid / 4;
+
+ /* fill right stereo command */
+ /* zero 2 bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd &=
+ ~(0x3 << (vc * 2 + PIX1 * 8));
+ /* set cmd bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd |=
+ (cmd_bits << (vc * 2 + PIX1 * 8));
+ }
}
/* cmd for PIX0, PIX1, RDI0, RDI1 */
if (ispif->applied_intf_cmd[vfe_intf].intf_cmd != 0xFFFFFFFF)
@@ -1024,7 +1103,7 @@ static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits,
}
static int msm_ispif_stop_immediately(struct ispif_device *ispif,
- struct msm_ispif_param_data *params)
+ struct msm_ispif_param_data_ext *params)
{
int i, rc = 0;
uint16_t cid_mask = 0;
@@ -1052,13 +1131,22 @@ static int msm_ispif_stop_immediately(struct ispif_device *ispif,
&params->entries[i]);
msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
cid_mask, params->entries[i].vfe_intf, 0);
+ if (params->stereo_enable) {
+ cid_mask = msm_ispif_get_right_cids_mask_from_cfg(
+ &params->right_entries[i],
+ params->entries[i].num_cids);
+ if (cid_mask)
+ msm_ispif_enable_intf_cids(ispif,
+ params->entries[i].intftype, cid_mask,
+ params->entries[i].vfe_intf, 0);
+ }
}
return rc;
}
static int msm_ispif_start_frame_boundary(struct ispif_device *ispif,
- struct msm_ispif_param_data *params)
+ struct msm_ispif_param_data_ext *params)
{
int rc = 0;
@@ -1074,13 +1162,14 @@ static int msm_ispif_start_frame_boundary(struct ispif_device *ispif,
rc = -EINVAL;
return rc;
}
+ msm_ispif_config_stereo(ispif, params);
msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params);
return rc;
}
static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif,
- struct msm_ispif_param_data *params)
+ struct msm_ispif_param_data_ext *params)
{
int rc = 0, i;
long timeout = 0;
@@ -1222,10 +1311,11 @@ end:
}
static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif,
- struct msm_ispif_param_data *params)
+ struct msm_ispif_param_data_ext *params)
{
int i, rc = 0;
uint16_t cid_mask = 0;
+ uint16_t cid_right_mask = 0;
uint32_t intf_addr;
enum msm_ispif_vfe_intf vfe_intf;
uint32_t stop_flag = 0;
@@ -1263,6 +1353,13 @@ static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif,
for (i = 0; i < params->num; i++) {
cid_mask =
msm_ispif_get_cids_mask_from_cfg(&params->entries[i]);
+ if (params->stereo_enable)
+ cid_right_mask =
+ msm_ispif_get_right_cids_mask_from_cfg(
+ &params->right_entries[i],
+ params->entries[i].num_cids);
+ else
+ cid_right_mask = 0;
vfe_intf = params->entries[i].vfe_intf;
switch (params->entries[i].intftype) {
@@ -1294,10 +1391,24 @@ static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif,
ISPIF_TIMEOUT_ALL_US);
if (rc < 0)
goto end;
+ if (cid_right_mask) {
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1);
+ rc = readl_poll_timeout(ispif->base + intf_addr,
+ stop_flag,
+ (stop_flag & 0xF) == 0xF,
+ ISPIF_TIMEOUT_SLEEP_US,
+ ISPIF_TIMEOUT_ALL_US);
+ if (rc < 0)
+ goto end;
+ }
/* disable CIDs in CID_MASK register */
msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
cid_mask, vfe_intf, 0);
+ if (cid_right_mask)
+ msm_ispif_enable_intf_cids(ispif,
+ params->entries[i].intftype, cid_right_mask,
+ params->entries[i].vfe_intf, 0);
}
end:
@@ -1318,6 +1429,14 @@ static void ispif_process_irq(struct ispif_device *ispif,
ispif->sof_count[vfe_id].sof_cnt[PIX0]++;
ispif->ispif_sof_debug++;
}
+ if (out[vfe_id].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_PIX_SOF_MASK) {
+ if (ispif->ispif_sof_debug < ISPIF_SOF_DEBUG_COUNT*2)
+ pr_err("%s: PIX1 frame id: %u\n", __func__,
+ ispif->sof_count[vfe_id].sof_cnt[PIX1]);
+ ispif->sof_count[vfe_id].sof_cnt[PIX1]++;
+ ispif->ispif_sof_debug++;
+ }
if (out[vfe_id].ispifIrqStatus0 &
ISPIF_IRQ_STATUS_RDI0_SOF_MASK) {
if (ispif->ispif_rdi0_debug < ISPIF_SOF_DEBUG_COUNT)
@@ -1344,12 +1463,55 @@ static void ispif_process_irq(struct ispif_device *ispif,
}
}
+static int msm_ispif_reconfig_3d_output(struct ispif_device *ispif,
+ enum msm_ispif_vfe_intf vfe_id)
+{
+ uint32_t reg_data;
+
+ if (WARN_ON(!ispif))
+ return -EINVAL;
+
+ if (!((vfe_id == VFE0) || (vfe_id == VFE1))) {
+ pr_err("%s;%d Cannot reconfigure 3D mode for VFE%d", __func__,
+ __LINE__, vfe_id);
+ return -EINVAL;
+ }
+ pr_info("%s;%d Reconfiguring 3D mode for VFE%d", __func__, __LINE__,
+ vfe_id);
+ reg_data = 0xFFFCFFFC;
+ msm_camera_io_w_mb(reg_data, ispif->base +
+ ISPIF_VFE_m_INTF_CMD_0(vfe_id));
+ msm_camera_io_w_mb(reg_data, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ if (vfe_id == VFE0) {
+ reg_data = 0;
+ reg_data |= (PIX_0_VFE_RST_STB | PIX_1_VFE_RST_STB |
+ STROBED_RST_EN | PIX_0_CSID_RST_STB |
+ PIX_1_CSID_RST_STB | PIX_OUTPUT_0_MISR_RST_STB);
+ msm_camera_io_w_mb(reg_data, ispif->base + ISPIF_RST_CMD_ADDR);
+ } else {
+ reg_data = 0;
+ reg_data |= (PIX_0_VFE_RST_STB | PIX_1_VFE_RST_STB |
+ STROBED_RST_EN | PIX_0_CSID_RST_STB |
+ PIX_1_CSID_RST_STB | PIX_OUTPUT_0_MISR_RST_STB);
+ msm_camera_io_w_mb(reg_data, ispif->base +
+ ISPIF_RST_CMD_1_ADDR);
+ }
+
+ reg_data = 0xFFFDFFFD;
+ msm_camera_io_w_mb(reg_data, ispif->base +
+ ISPIF_VFE_m_INTF_CMD_0(vfe_id));
+ return 0;
+}
+
static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
void *data)
{
struct ispif_device *ispif = (struct ispif_device *)data;
bool fatal_err = false;
int i = 0;
+ uint32_t reg_data;
BUG_ON(!ispif);
BUG_ON(!out);
@@ -1400,6 +1562,12 @@ static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
fatal_err = true;
}
+ if (out[VFE0].ispifIrqStatus1 & PIX_INTF_1_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE0 pix1 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
if (out[VFE0].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ) {
pr_err_ratelimited("%s: VFE0 rdi0 overflow.\n",
__func__);
@@ -1432,6 +1600,12 @@ static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
fatal_err = true;
}
+ if (out[VFE1].ispifIrqStatus1 & PIX_INTF_1_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE1 pix1 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
if (out[VFE1].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ) {
pr_err_ratelimited("%s: VFE1 rdi0 overflow.\n",
__func__);
@@ -1453,6 +1627,43 @@ static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
ispif_process_irq(ispif, out, VFE1);
}
+ if ((out[VFE0].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) ||
+ (out[VFE0].ispifIrqStatus1 & PIX_INTF_0_OVERFLOW_IRQ) ||
+ (out[VFE0].ispifIrqStatus2 & (L_R_SOF_MISMATCH_ERR_IRQ |
+ L_R_EOF_MISMATCH_ERR_IRQ | L_R_SOL_MISMATCH_ERR_IRQ))) {
+ reg_data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_OUTPUT_SEL(VFE0));
+ if ((reg_data & 0x03) == VFE_PIX_INTF_SEL_3D) {
+ pix_overflow_error_count[VFE0]++;
+ if (pix_overflow_error_count[VFE0] >=
+ MAX_PIX_OVERFLOW_ERROR_COUNT) {
+ msm_ispif_reconfig_3d_output(ispif, VFE0);
+ pix_overflow_error_count[VFE0] = 0;
+ }
+ fatal_err = false;
+ }
+ }
+
+ if (ispif->vfe_info.num_vfe > 1) {
+ if ((out[VFE1].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) ||
+ (out[VFE1].ispifIrqStatus1 & PIX_INTF_0_OVERFLOW_IRQ) ||
+ (out[VFE1].ispifIrqStatus2 & (L_R_SOF_MISMATCH_ERR_IRQ |
+ L_R_EOF_MISMATCH_ERR_IRQ | L_R_SOL_MISMATCH_ERR_IRQ))) {
+ reg_data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_OUTPUT_SEL(VFE1));
+ if ((reg_data & 0x03) == VFE_PIX_INTF_SEL_3D) {
+ pix_overflow_error_count[VFE1]++;
+ if (pix_overflow_error_count[VFE1] >=
+ MAX_PIX_OVERFLOW_ERROR_COUNT) {
+ msm_ispif_reconfig_3d_output(ispif,
+ VFE1);
+ pix_overflow_error_count[VFE1] = 0;
+ }
+ }
+ fatal_err = false;
+ }
+ }
+
if (fatal_err == true) {
pr_err_ratelimited("%s: fatal error, stop ispif immediately\n",
__func__);
@@ -1561,61 +1772,97 @@ static void msm_ispif_release(struct ispif_device *ispif)
pr_err("%s: failed to remove vote for AHB\n", __func__);
}
-static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg)
+static long msm_ispif_dispatch_cmd(enum ispif_cfg_type_t cmd,
+ struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params)
{
long rc = 0;
- struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg;
- struct ispif_device *ispif =
- (struct ispif_device *)v4l2_get_subdevdata(sd);
-
- BUG_ON(!sd);
- BUG_ON(!pcdata);
- mutex_lock(&ispif->mutex);
- switch (pcdata->cfg_type) {
- case ISPIF_ENABLE_REG_DUMP:
- ispif->enb_dump_reg = pcdata->reg_dump; /* save dump config */
- break;
- case ISPIF_INIT:
- rc = msm_ispif_init(ispif, pcdata->csid_version);
- msm_ispif_io_dump_reg(ispif);
- break;
+ switch (cmd) {
case ISPIF_CFG:
- rc = msm_ispif_config(ispif, &pcdata->params);
+ rc = msm_ispif_config(ispif, params);
msm_ispif_io_dump_reg(ispif);
break;
case ISPIF_START_FRAME_BOUNDARY:
- rc = msm_ispif_start_frame_boundary(ispif, &pcdata->params);
+ rc = msm_ispif_start_frame_boundary(ispif, params);
msm_ispif_io_dump_reg(ispif);
break;
case ISPIF_RESTART_FRAME_BOUNDARY:
- rc = msm_ispif_restart_frame_boundary(ispif, &pcdata->params);
+ rc = msm_ispif_restart_frame_boundary(ispif, params);
msm_ispif_io_dump_reg(ispif);
break;
-
case ISPIF_STOP_FRAME_BOUNDARY:
- rc = msm_ispif_stop_frame_boundary(ispif, &pcdata->params);
+ rc = msm_ispif_stop_frame_boundary(ispif, params);
msm_ispif_io_dump_reg(ispif);
break;
case ISPIF_STOP_IMMEDIATELY:
- rc = msm_ispif_stop_immediately(ispif, &pcdata->params);
+ rc = msm_ispif_stop_immediately(ispif, params);
msm_ispif_io_dump_reg(ispif);
break;
case ISPIF_RELEASE:
msm_ispif_reset(ispif);
msm_ispif_reset_hw(ispif);
break;
- case ISPIF_SET_VFE_INFO:
- rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info);
+ case ISPIF_CFG2:
+ rc = msm_ispif_config2(ispif, params);
+ msm_ispif_io_dump_reg(ispif);
break;
default:
pr_err("%s: invalid cfg_type\n", __func__);
rc = -EINVAL;
break;
}
+ return rc;
+}
+
+static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg)
+{
+ long rc = 0;
+ struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg;
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+ int i;
+ struct msm_ispif_param_data_ext params;
+
+ if (WARN_ON(!sd) || WARN_ON(!pcdata))
+ return -EINVAL;
+
+ mutex_lock(&ispif->mutex);
+ switch (pcdata->cfg_type) {
+ case ISPIF_ENABLE_REG_DUMP:
+ /* save dump config */
+ ispif->enb_dump_reg = pcdata->reg_dump;
+ break;
+ case ISPIF_INIT:
+ rc = msm_ispif_init(ispif, pcdata->csid_version);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_SET_VFE_INFO:
+ rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info);
+ break;
+ default:
+ memset(&params, 0, sizeof(params));
+ if (pcdata->params.num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid num entries %u\n", __func__,
+ pcdata->params.num);
+ rc = -EINVAL;
+ } else {
+ params.num = pcdata->params.num;
+ for (i = 0; i < pcdata->params.num; i++)
+ memcpy(&params.entries[i],
+ &pcdata->params.entries[i],
+ sizeof(struct msm_ispif_params_entry));
+ params.stereo_enable = 0;
+ rc = msm_ispif_dispatch_cmd(pcdata->cfg_type, ispif,
+ &params);
+ }
+ break;
+ }
mutex_unlock(&ispif->mutex);
+
return rc;
}
+
static struct v4l2_file_operations msm_ispif_v4l2_subdev_fops;
static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd,
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h
index d488ca618537..49d7d0f7624e 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h
@@ -87,6 +87,12 @@
#define MISC_LOGIC_RST_STB BIT(1)
#define STROBED_RST_EN BIT(0)
+#define VFE_PIX_INTF_SEL_3D 0x3
+#define PIX_OUTPUT_0_MISR_RST_STB BIT(16)
+#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16)
+#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17)
+#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18)
+
#define ISPIF_RST_CMD_MASK 0xFE1C77FF
#define ISPIF_RST_CMD_1_MASK 0xFFFFFFFF /* undefined */
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h
index 8ae61dc2d4f6..9abf55efc46c 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h
@@ -22,6 +22,7 @@
#define ISPIF_VFE(m) ((m) * 0x200)
#define ISPIF_VFE_m_CTRL_0(m) (0x200 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_CTRL_1(m) (0x204 + ISPIF_VFE(m))
#define ISPIF_VFE_m_IRQ_MASK_0(m) (0x208 + ISPIF_VFE(m))
#define ISPIF_VFE_m_IRQ_MASK_1(m) (0x20C + ISPIF_VFE(m))
#define ISPIF_VFE_m_IRQ_MASK_2(m) (0x210 + ISPIF_VFE(m))
@@ -71,6 +72,12 @@
#define MISC_LOGIC_RST_STB BIT(1)
#define STROBED_RST_EN BIT(0)
+#define VFE_PIX_INTF_SEL_3D 0x3
+#define PIX_OUTPUT_0_MISR_RST_STB BIT(16)
+#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16)
+#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17)
+#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18)
+
#define ISPIF_RST_CMD_MASK 0xFE0F1FFF
#define ISPIF_RST_CMD_1_MASK 0xFC0F1FF9
@@ -78,6 +85,7 @@
#define ISPIF_RST_CMD_1_MASK_RESTART 0x00001FF9
#define PIX_INTF_0_OVERFLOW_IRQ BIT(12)
+#define PIX_INTF_1_OVERFLOW_IRQ BIT(12)
#define RAW_INTF_0_OVERFLOW_IRQ BIT(25)
#define RAW_INTF_1_OVERFLOW_IRQ BIT(25)
#define RAW_INTF_2_OVERFLOW_IRQ BIT(12)
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h
index 94cc974441ee..5f2aa06f3e13 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h
@@ -74,6 +74,12 @@
#define MISC_LOGIC_RST_STB BIT(1)
#define STROBED_RST_EN BIT(0)
+#define VFE_PIX_INTF_SEL_3D 0x3
+#define PIX_OUTPUT_0_MISR_RST_STB BIT(16)
+#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16)
+#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17)
+#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18)
+
#define ISPIF_RST_CMD_MASK 0xFE7F1FFF
#define ISPIF_RST_CMD_1_MASK 0xFC7F1FF9
@@ -81,6 +87,7 @@
#define ISPIF_RST_CMD_1_MASK_RESTART 0x7F1FF9
#define PIX_INTF_0_OVERFLOW_IRQ BIT(12)
+#define PIX_INTF_1_OVERFLOW_IRQ BIT(12)
#define RAW_INTF_0_OVERFLOW_IRQ BIT(25)
#define RAW_INTF_1_OVERFLOW_IRQ BIT(25)
#define RAW_INTF_2_OVERFLOW_IRQ BIT(12)
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
index 76a7c6942c68..af6206f83048 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -553,8 +553,7 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd,
sizeof(struct msm_buf_mngr_info))) {
return -EFAULT;
}
- MSM_CAM_GET_IOCTL_ARG_PTR(&k_ioctl.ioctl_ptr,
- &buf_info, sizeof(void *));
+ k_ioctl.ioctl_ptr = (uintptr_t)&buf_info;
argp = &k_ioctl;
rc = msm_cam_buf_mgr_ops(cmd, argp);
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index b067c4916341..75043e1b0427 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -47,6 +47,8 @@
#define CCI_DBG(fmt, args...) pr_debug(fmt, ##args)
#endif
+#define CCI_DUMP_REG 0
+
/* Max bytes that can be read per CCI read transaction */
#define CCI_READ_MAX 12
#define CCI_I2C_READ_MAX_RETRIES 3
@@ -201,6 +203,7 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
enum cci_i2c_queue_t queue)
{
int32_t rc = 0;
+ unsigned long flags;
uint32_t read_val = 0;
uint32_t reg_offset = master * 0x200 + queue * 0x100;
read_val = msm_camera_io_r_mb(cci_dev->base +
@@ -223,6 +226,8 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
reg_val = 1 << ((master * 2) + queue);
CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
atomic_set(&cci_dev->cci_master_info[master].
done_pending[queue], 1);
msm_camera_io_w_mb(reg_val, cci_dev->base +
@@ -230,6 +235,8 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
CDBG("%s line %d wait_for_completion_timeout\n",
__func__, __LINE__);
atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = wait_for_completion_timeout(&cci_dev->
cci_master_info[master].report_q[queue], CCI_TIMEOUT);
if (rc <= 0) {
@@ -291,6 +298,9 @@ static uint32_t msm_cci_wait(struct cci_device *cci_dev,
__func__, __LINE__);
if (rc <= 0) {
+ if (CCI_DUMP_REG)
+ msm_cci_dump_registers(cci_dev, master, queue);
+
pr_err("%s: %d wait for queue: %d\n",
__func__, __LINE__, queue);
if (rc == 0)
@@ -438,10 +448,17 @@ static int32_t msm_cci_wait_report_cmd(struct cci_device *cci_dev,
enum cci_i2c_master_t master,
enum cci_i2c_queue_t queue)
{
+ unsigned long flags;
uint32_t reg_val = 1 << ((master * 2) + queue);
msm_cci_load_report_cmd(cci_dev, master, queue);
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+
msm_camera_io_w_mb(reg_val, cci_dev->base +
CCI_QUEUE_START_ADDR);
return msm_cci_wait(cci_dev, master, queue);
@@ -451,13 +468,19 @@ static void msm_cci_process_half_q(struct cci_device *cci_dev,
enum cci_i2c_master_t master,
enum cci_i2c_queue_t queue)
{
+ unsigned long flags;
uint32_t reg_val = 1 << ((master * 2) + queue);
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
msm_cci_load_report_cmd(cci_dev, master, queue);
atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
msm_camera_io_w_mb(reg_val, cci_dev->base +
CCI_QUEUE_START_ADDR);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
}
static int32_t msm_cci_process_full_q(struct cci_device *cci_dev,
@@ -465,15 +488,23 @@ static int32_t msm_cci_process_full_q(struct cci_device *cci_dev,
enum cci_i2c_queue_t queue)
{
int32_t rc = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
if (1 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
atomic_set(&cci_dev->cci_master_info[master].
done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = msm_cci_wait(cci_dev, master, queue);
if (rc < 0) {
pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
return rc;
}
} else {
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = msm_cci_wait_report_cmd(cci_dev, master, queue);
if (rc < 0) {
pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
@@ -501,8 +532,13 @@ static int32_t msm_cci_transfer_end(struct cci_device *cci_dev,
enum cci_i2c_queue_t queue)
{
int32_t rc = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = msm_cci_lock_queue(cci_dev, master, queue, 0);
if (rc < 0) {
pr_err("%s failed line %d\n", __func__, __LINE__);
@@ -516,6 +552,8 @@ static int32_t msm_cci_transfer_end(struct cci_device *cci_dev,
} else {
atomic_set(&cci_dev->cci_master_info[master].
done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = msm_cci_wait(cci_dev, master, queue);
if (rc < 0) {
pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
@@ -570,6 +608,7 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev,
uint32_t reg_offset;
uint32_t val = 0;
uint32_t max_queue_size;
+ unsigned long flags;
if (i2c_cmd == NULL) {
pr_err("%s:%d Failed line\n", __func__,
@@ -613,7 +652,11 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev,
msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
reg_offset);
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
max_queue_size = cci_dev->cci_i2c_queue_info[master][queue].
max_queue_size;
@@ -889,7 +932,9 @@ static int32_t msm_cci_i2c_read(struct v4l2_subdev *sd,
rc = wait_for_completion_timeout(&cci_dev->
cci_master_info[master].reset_complete, CCI_TIMEOUT);
if (rc <= 0) {
- msm_cci_dump_registers(cci_dev, master, queue);
+ if (CCI_DUMP_REG)
+ msm_cci_dump_registers(cci_dev, master, queue);
+
if (rc == 0)
rc = -ETIMEDOUT;
pr_err("%s: %d wait_for_completion_timeout rc = %d\n",
@@ -1641,6 +1686,7 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd,
static irqreturn_t msm_cci_irq(int irq_num, void *data)
{
uint32_t irq;
+ unsigned long flags;
struct cci_device *cci_dev = data;
irq = msm_camera_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR);
msm_camera_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
@@ -1667,22 +1713,30 @@ static irqreturn_t msm_cci_irq(int irq_num, void *data)
if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) {
struct msm_camera_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_0], flags);
atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
cci_master_info->status = 0;
if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
complete(&cci_master_info->report_q[QUEUE_0]);
atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_0], flags);
}
if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) {
struct msm_camera_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_1], flags);
atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
cci_master_info->status = 0;
if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
complete(&cci_master_info->report_q[QUEUE_1]);
atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_1], flags);
}
if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) {
cci_dev->cci_master_info[MASTER_1].status = 0;
@@ -1691,22 +1745,30 @@ static irqreturn_t msm_cci_irq(int irq_num, void *data)
if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) {
struct msm_camera_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_0], flags);
atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
cci_master_info->status = 0;
if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
complete(&cci_master_info->report_q[QUEUE_0]);
atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_0], flags);
}
if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) {
struct msm_camera_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_1], flags);
atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
cci_master_info->status = 0;
if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
complete(&cci_master_info->report_q[QUEUE_1]);
atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_1], flags);
}
if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
@@ -1795,7 +1857,9 @@ static void msm_cci_init_cci_params(struct cci_device *new_cci_dev)
mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]);
init_completion(&new_cci_dev->
cci_master_info[i].report_q[j]);
- }
+ spin_lock_init(&new_cci_dev->
+ cci_master_info[i].lock_q[j]);
+ }
}
return;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
index 6e39d814bd73..eb615cc7a62c 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -125,6 +125,7 @@ struct msm_camera_cci_master_info {
struct mutex mutex_q[NUM_QUEUES];
struct completion report_q[NUM_QUEUES];
atomic_t done_pending[NUM_QUEUES];
+ spinlock_t lock_q[NUM_QUEUES];
};
struct msm_cci_clk_params_t {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
index 55a743737c59..7ae071176ef4 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
@@ -227,16 +227,34 @@ static void msm_csid_set_sof_freeze_debug_reg(
static int msm_csid_reset(struct csid_device *csid_dev)
{
int32_t rc = 0;
+ uint32_t irq = 0, irq_bitshift;
+
+ irq_bitshift = csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift;
msm_camera_io_w(csid_dev->ctrl_reg->csid_reg.csid_rst_stb_all,
csid_dev->base +
csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr);
rc = wait_for_completion_timeout(&csid_dev->reset_complete,
CSID_TIMEOUT);
- if (rc <= 0) {
+ if (rc < 0) {
pr_err("wait_for_completion in msm_csid_reset fail rc = %d\n",
rc);
+ } else if (rc == 0) {
+ irq = msm_camera_io_r(csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr);
+ pr_err_ratelimited("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+ __func__, csid_dev->pdev->id, irq);
+ if (irq & (0x1 << irq_bitshift)) {
+ rc = 1;
+ CDBG("%s succeeded", __func__);
+ } else {
+ rc = 0;
+ pr_err("%s reset csid_irq_status failed = 0x%x\n",
+ __func__, irq);
+ }
if (rc == 0)
rc = -ETIMEDOUT;
+ } else {
+ CDBG("%s succeeded", __func__);
}
return rc;
}
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 5d57ec8c28ff..8f55f453bf03 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
@@ -89,6 +89,7 @@ struct msm_sensor_ctrl_t {
uint32_t set_mclk_23880000;
uint8_t is_csid_tg_mode;
uint32_t is_secure;
+ uint8_t bypass_video_node_creation;
};
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 1dd2b0d26007..344f1a6f8d92 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
@@ -86,11 +86,14 @@ static int32_t msm_sensor_driver_create_i2c_v4l_subdev
struct i2c_client *client = s_ctrl->sensor_i2c_client->client;
CDBG("%s %s I2c probe succeeded\n", __func__, client->name);
- rc = camera_init_v4l2(&client->dev, &session_id);
- if (rc < 0) {
- pr_err("failed: camera_init_i2c_v4l2 rc %d", rc);
- return rc;
+ if (s_ctrl->bypass_video_node_creation == 0) {
+ rc = camera_init_v4l2(&client->dev, &session_id);
+ if (rc < 0) {
+ pr_err("failed: camera_init_i2c_v4l2 rc %d", rc);
+ return rc;
+ }
}
+
CDBG("%s rc %d session_id %d\n", __func__, rc, session_id);
snprintf(s_ctrl->msm_sd.sd.name,
sizeof(s_ctrl->msm_sd.sd.name), "%s",
@@ -123,11 +126,14 @@ static int32_t msm_sensor_driver_create_v4l_subdev
int32_t rc = 0;
uint32_t session_id = 0;
- rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id);
- if (rc < 0) {
- pr_err("failed: camera_init_v4l2 rc %d", rc);
- return rc;
+ if (s_ctrl->bypass_video_node_creation == 0) {
+ rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id);
+ if (rc < 0) {
+ pr_err("failed: camera_init_v4l2 rc %d", rc);
+ return rc;
+ }
}
+
CDBG("rc %d session_id %d", rc, session_id);
s_ctrl->sensordata->sensor_info->session_id = session_id;
@@ -773,6 +779,8 @@ int32_t msm_sensor_driver_probe(void *setting,
slave_info32->sensor_init_params;
slave_info->output_format =
slave_info32->output_format;
+ slave_info->bypass_video_node_creation =
+ !!slave_info32->bypass_video_node_creation;
kfree(slave_info32);
} else
#endif
@@ -800,7 +808,8 @@ int32_t msm_sensor_driver_probe(void *setting,
slave_info->sensor_init_params.position);
CDBG("mount %d",
slave_info->sensor_init_params.sensor_mount_angle);
-
+ CDBG("bypass video node creation %d",
+ slave_info->bypass_video_node_creation);
/* Validate camera id */
if (slave_info->camera_id >= MAX_CAMERAS) {
pr_err("failed: invalid camera id %d max %d",
@@ -980,6 +989,9 @@ CSID_TG:
*/
s_ctrl->is_probe_succeed = 1;
+ s_ctrl->bypass_video_node_creation =
+ slave_info->bypass_video_node_creation;
+
/*
* Create /dev/videoX node, comment for now until dummy /dev/videoX
* node is created and used by HAL
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 0cd8e613c224..cfee4efb6f16 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -465,6 +465,10 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q)
sde_rot_mgr_lock(rot_dev->mgr);
sde_rotator_cancel_all_requests(rot_dev->mgr, ctx->private);
sde_rot_mgr_unlock(rot_dev->mgr);
+ mutex_unlock(q->lock);
+ cancel_work_sync(&ctx->submit_work);
+ cancel_work_sync(&ctx->retire_work);
+ mutex_lock(q->lock);
}
sde_rotator_return_all_buffers(q, VB2_BUF_STATE_ERROR);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
index 3b36b6bc76de..5eaa2910228e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -507,10 +507,15 @@ struct sde_mdp_format_params *sde_get_format_params(u32 format)
if (!fmt_found) {
for (i = 0; i < ARRAY_SIZE(sde_mdp_format_ubwc_map); i++) {
fmt = &sde_mdp_format_ubwc_map[i].mdp_format;
- if (format == fmt->format)
+ if (format == fmt->format) {
+ fmt_found = true;
break;
+ }
}
}
+ /* If format not supported than return NULL */
+ if (!fmt_found)
+ fmt = NULL;
return fmt;
}
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 c3a0cfb390c4..34ec6529d8ae 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -1245,6 +1245,33 @@ static int sde_hw_rotator_swts_create(struct sde_hw_rotator *rot)
goto err_detach;
}
+ ion_free(rot->iclient, handle);
+
+ sde_smmu_ctrl(0);
+
+ return rc;
+err_detach:
+ dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
+err_put:
+ dma_buf_put(data->srcp_dma_buf);
+ data->srcp_dma_buf = NULL;
+imap_err:
+ ion_free(rot->iclient, handle);
+
+ return rc;
+}
+
+/*
+ * sde_hw_rotator_swts_map - map software timestamp buffer
+ * @rot: Pointer to rotator hw
+ *
+ */
+static int sde_hw_rotator_swts_map(struct sde_hw_rotator *rot)
+{
+ int rc = 0;
+ struct sde_mdp_img_data *data = &rot->swts_buf;
+
+ sde_smmu_ctrl(1);
rc = sde_smmu_map_dma_buf(data->srcp_dma_buf, data->srcp_table,
SDE_IOMMU_DOMAIN_ROT_UNSECURE, &data->addr,
&data->len, DMA_BIDIRECTIONAL);
@@ -1264,35 +1291,25 @@ static int sde_hw_rotator_swts_create(struct sde_hw_rotator *rot)
data->mapped = true;
SDEROT_DBG("swts buffer mapped: %pad/%lx va:%p\n", &data->addr,
- data->len, rot->swts_buffer);
-
- ion_free(rot->iclient, handle);
-
+ data->len, rot->swts_buffer);
sde_smmu_ctrl(0);
-
return rc;
+
kmap_err:
sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE,
DMA_FROM_DEVICE, data->srcp_dma_buf);
err_unmap:
dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
DMA_FROM_DEVICE);
-err_detach:
- dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
-err_put:
- dma_buf_put(data->srcp_dma_buf);
- data->srcp_dma_buf = NULL;
-imap_err:
- ion_free(rot->iclient, handle);
return rc;
}
/*
- * sde_hw_rotator_swtc_destroy - destroy software timestamp buffer
+ * sde_hw_rotator_swtc_unmap - unmap software timestamp buffer
* @rot: Pointer to rotator hw
*/
-static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot)
+static void sde_hw_rotator_swtc_unmap(struct sde_hw_rotator *rot)
{
struct sde_mdp_img_data *data;
@@ -1301,9 +1318,29 @@ static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot)
dma_buf_end_cpu_access(data->srcp_dma_buf, 0, data->len,
DMA_FROM_DEVICE);
dma_buf_kunmap(data->srcp_dma_buf, 0, rot->swts_buffer);
+ rot->swts_buffer = NULL;
sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE,
DMA_FROM_DEVICE, data->srcp_dma_buf);
+ data->addr = 0x0;
+ data->len = 0;
+
+ data->mapped = false;
+}
+
+/*
+ * sde_hw_rotator_swtc_destroy - destroy software timestamp buffer
+ * @rot: Pointer to rotator hw
+ */
+static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot)
+{
+ struct sde_mdp_img_data *data;
+
+ data = &rot->swts_buf;
+
+ if (data->mapped)
+ sde_hw_rotator_swtc_unmap(rot);
+
dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
DMA_FROM_DEVICE);
dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
@@ -1436,6 +1473,7 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
struct sde_rot_mgr *mgr, u32 pipe_id, u32 wb_id)
{
struct sde_hw_rotator_resource_info *resinfo;
+ int ret = 0;
if (!mgr || !mgr->hw_data) {
SDEROT_ERR("null parameters\n");
@@ -1463,8 +1501,21 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
else {
resinfo->hw.max_active = SDE_HW_ROT_REGDMA_TOTAL_CTX - 1;
- if (resinfo->rot->iclient == NULL)
- sde_hw_rotator_swts_create(resinfo->rot);
+ if (resinfo->rot->iclient == NULL) {
+ ret = sde_hw_rotator_swts_create(resinfo->rot);
+ if (ret) {
+ SDEROT_ERR("swts buffer create failed\n");
+ goto swts_fail;
+ }
+ }
+
+ if (resinfo->rot->swts_buf.mapped == false) {
+ ret = sde_hw_rotator_swts_map(resinfo->rot);
+ if (ret) {
+ SDEROT_ERR("swts buffer map failed\n");
+ goto swts_fail;
+ }
+ }
}
if (resinfo->rot->irq_num >= 0)
@@ -1474,6 +1525,10 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
resinfo, wb_id);
return &resinfo->hw;
+
+swts_fail:
+ devm_kfree(&mgr->pdev->dev, resinfo);
+ return NULL;
}
/*
@@ -1485,6 +1540,7 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
struct sde_rot_hw_resource *hw)
{
struct sde_hw_rotator_resource_info *resinfo;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
if (!mgr || !mgr->hw_data)
return;
@@ -1499,6 +1555,18 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
if (resinfo->rot->irq_num >= 0)
sde_hw_rotator_disable_irq(resinfo->rot);
+ /*
+ * For SDM660 and SDM630, the IOMMU is shared between MDP and rotator.
+ * If IOMMU is detached from MDP driver, the timestamp buffer will be
+ * invalidated. It is safer to unmap the timestamp buffer when the
+ * rotator session ends, so that it will be mapped again when a fresh
+ * session starts.
+ */
+ if (((mdata->mdss_version == MDSS_MDP_HW_REV_320) ||
+ (mdata->mdss_version == MDSS_MDP_HW_REV_330)) &&
+ resinfo->rot->swts_buf.mapped)
+ sde_hw_rotator_swtc_unmap(resinfo->rot);
+
devm_kfree(&mgr->pdev->dev, resinfo);
}
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index a63279715de6..a8dc1d010d62 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -766,6 +766,8 @@ static int __init msm_vidc_init(void)
if (rc) {
dprintk(VIDC_ERR,
"Failed to register platform driver\n");
+ msm_vidc_debugfs_deinit_drv();
+ debugfs_remove_recursive(vidc_driver->debugfs_root);
kfree(vidc_driver);
vidc_driver = NULL;
}
@@ -776,6 +778,7 @@ static int __init msm_vidc_init(void)
static void __exit msm_vidc_exit(void)
{
platform_driver_unregister(&msm_vidc_driver);
+ msm_vidc_debugfs_deinit_drv();
debugfs_remove_recursive(vidc_driver->debugfs_root);
mutex_destroy(&vidc_driver->lock);
kfree(vidc_driver);
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index ca9d7fba4ee3..30726354164b 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -3511,6 +3511,48 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
frameqp = ctrl->val;
pdata = &frameqp;
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP:
+ {
+ rc = msm_venc_validate_qp_value(inst, ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial I QP\n");
+ break;
+ }
+ /*
+ * Defer sending property from here, set_ext_ctrl
+ * will send it based on the rc value.
+ */
+ property_id = 0;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP:
+ {
+ rc = msm_venc_validate_qp_value(inst, ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial B QP\n");
+ break;
+ }
+ /*
+ * Defer sending property from here, set_ext_ctrl
+ * will send it based on the rc value.
+ */
+ property_id = 0;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP:
+ {
+ rc = msm_venc_validate_qp_value(inst, ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial P QP\n");
+ break;
+ }
+ /*
+ * Defer sending property from here, set_ext_ctrl
+ * will send it based on the rc value.
+ */
+ property_id = 0;
+ break;
+ }
case V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI:
property_id = HAL_PARAM_VENC_VQZIP_SEI;
enable.enable = ctrl->val;
@@ -3745,7 +3787,7 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
struct hal_aspect_ratio sar;
struct hal_bitrate bitrate;
struct hal_frame_size blur_res;
- struct v4l2_ctrl *temp_ctrl;
+ struct v4l2_control temp_ctrl;
if (!inst || !inst->core || !inst->core->device || !ctrl) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -3812,12 +3854,15 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
/* Sanity check for the QP boundaries as we are using
* same control to set Initial QP for all the codecs
*/
- temp_ctrl->id =
+ temp_ctrl.id =
V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP;
- temp_ctrl->val = control[i].value;
- rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ temp_ctrl.value = control[i].value;
+
+ rc = msm_comm_s_ctrl(inst, &temp_ctrl);
if (rc) {
- dprintk(VIDC_ERR, "Invalid Initial I QP\n");
+ dprintk(VIDC_ERR,
+ "%s Failed setting Initial I Frame QP : %d\n",
+ __func__, rc);
break;
}
quant.qpi = control[i].value;
@@ -3825,12 +3870,14 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
pdata = &quant;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP:
- temp_ctrl->id =
+ temp_ctrl.id =
V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP;
- temp_ctrl->val = control[i].value;
- rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ temp_ctrl.value = control[i].value;
+ rc = msm_comm_s_ctrl(inst, &temp_ctrl);
if (rc) {
- dprintk(VIDC_ERR, "Invalid Initial P QP\n");
+ dprintk(VIDC_ERR,
+ "%s Failed setting Initial P Frame QP : %d\n",
+ __func__, rc);
break;
}
quant.qpp = control[i].value;
@@ -3838,12 +3885,14 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
pdata = &quant;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP:
- temp_ctrl->id =
+ temp_ctrl.id =
V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP;
- temp_ctrl->val = control[i].value;
- rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ temp_ctrl.value = control[i].value;
+ rc = msm_comm_s_ctrl(inst, &temp_ctrl);
if (rc) {
- dprintk(VIDC_ERR, "Invalid Initial B QP\n");
+ dprintk(VIDC_ERR,
+ "%s Failed setting Initial B Frame QP : %d\n",
+ __func__, rc);
break;
}
quant.qpb = control[i].value;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index 1248a1c08103..a9b367d6fe93 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -38,6 +38,7 @@ bool msm_vidc_debug_timeout = false;
#define MAX_DBG_BUF_SIZE 4096
struct debug_buffer {
+ struct mutex lock;
char ptr[MAX_DBG_BUF_SIZE];
char *curr;
u32 filled_size;
@@ -64,8 +65,12 @@ static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...)
{
va_list args;
u32 size;
+
+ char *curr = buffer->curr;
+ char *end = buffer->ptr + MAX_DBG_BUF_SIZE;
+
va_start(args, fmt);
- size = vscnprintf(buffer->curr, MAX_DBG_BUF_SIZE - 1, fmt, args);
+ size = vscnprintf(curr, end - curr, fmt, args);
va_end(args);
buffer->curr += size;
buffer->filled_size += size;
@@ -79,12 +84,15 @@ static ssize_t core_info_read(struct file *file, char __user *buf,
struct hfi_device *hdev;
struct hal_fw_info fw_info = { {0} };
int i = 0, rc = 0;
+ ssize_t len = 0;
if (!core || !core->device) {
dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core);
return 0;
}
hdev = core->device;
+
+ mutex_lock(&dbg_buf.lock);
INIT_DBG_BUF(dbg_buf);
write_str(&dbg_buf, "===============================\n");
write_str(&dbg_buf, "CORE %d: %pK\n", core->id, core);
@@ -108,8 +116,11 @@ err_fw_info:
completion_done(&core->completions[SYS_MSG_INDEX(i)]) ?
"pending" : "done");
}
- return simple_read_from_buffer(buf, count, ppos,
+ len = simple_read_from_buffer(buf, count, ppos,
dbg_buf.ptr, dbg_buf.filled_size);
+
+ mutex_unlock(&dbg_buf.lock);
+ return len;
}
static const struct file_operations core_info_fops = {
@@ -147,7 +158,10 @@ static const struct file_operations ssr_fops = {
struct dentry *msm_vidc_debugfs_init_drv(void)
{
bool ok = false;
- struct dentry *dir = debugfs_create_dir("msm_vidc", NULL);
+ struct dentry *dir = NULL;
+
+ mutex_init(&dbg_buf.lock);
+ dir = debugfs_create_dir("msm_vidc", NULL);
if (IS_ERR_OR_NULL(dir)) {
dir = NULL;
goto failed_create_dir;
@@ -216,6 +230,7 @@ struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n");
goto failed_create_dir;
}
+
if (!debugfs_create_file("info", S_IRUGO, dir, core, &core_info_fops)) {
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
goto failed_create_dir;
@@ -269,11 +284,14 @@ static ssize_t inst_info_read(struct file *file, char __user *buf,
{
struct msm_vidc_inst *inst = file->private_data;
int i, j;
+ ssize_t len = 0;
if (!inst) {
dprintk(VIDC_ERR, "Invalid params, inst %pK\n", inst);
return 0;
}
+
+ mutex_lock(&dbg_buf.lock);
INIT_DBG_BUF(dbg_buf);
write_str(&dbg_buf, "===============================\n");
write_str(&dbg_buf, "INSTANCE: %pK (%s)\n", inst,
@@ -331,8 +349,10 @@ static ssize_t inst_info_read(struct file *file, char __user *buf,
publish_unreleased_reference(inst);
- return simple_read_from_buffer(buf, count, ppos,
+ len = simple_read_from_buffer(buf, count, ppos,
dbg_buf.ptr, dbg_buf.filled_size);
+ mutex_unlock(&dbg_buf.lock);
+ return len;
}
static const struct file_operations inst_info_fops = {
@@ -413,3 +433,8 @@ void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
}
}
+void msm_vidc_debugfs_deinit_drv(void)
+{
+ mutex_destroy(&dbg_buf.lock);
+}
+
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
index 39ac6273f34e..853ce4b89f2b 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
@@ -126,6 +126,7 @@ struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
struct dentry *parent);
void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
enum msm_vidc_debugfs_event e);
+void msm_vidc_debugfs_deinit_drv(void);
static inline void tic(struct msm_vidc_inst *i, enum profiling_points p,
char *b)
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 5cefca95734e..885f689ac870 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1595,6 +1595,114 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain)
return buffer;
}
+static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+
+ chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+ if (chain == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&chain->entities);
+ mutex_init(&chain->ctrl_mutex);
+ chain->dev = dev;
+ v4l2_prio_init(&chain->prio);
+
+ return chain;
+}
+
+/*
+ * Fallback heuristic for devices that don't connect units and terminals in a
+ * valid chain.
+ *
+ * Some devices have invalid baSourceID references, causing uvc_scan_chain()
+ * to fail, but if we just take the entities we can find and put them together
+ * in the most sensible chain we can think of, turns out they do work anyway.
+ * Note: This heuristic assumes there is a single chain.
+ *
+ * At the time of writing, devices known to have such a broken chain are
+ * - Acer Integrated Camera (5986:055a)
+ * - Realtek rtl157a7 (0bda:57a7)
+ */
+static int uvc_scan_fallback(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+ struct uvc_entity *iterm = NULL;
+ struct uvc_entity *oterm = NULL;
+ struct uvc_entity *entity;
+ struct uvc_entity *prev;
+
+ /*
+ * Start by locating the input and output terminals. We only support
+ * devices with exactly one of each for now.
+ */
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (UVC_ENTITY_IS_ITERM(entity)) {
+ if (iterm)
+ return -EINVAL;
+ iterm = entity;
+ }
+
+ if (UVC_ENTITY_IS_OTERM(entity)) {
+ if (oterm)
+ return -EINVAL;
+ oterm = entity;
+ }
+ }
+
+ if (iterm == NULL || oterm == NULL)
+ return -EINVAL;
+
+ /* Allocate the chain and fill it. */
+ chain = uvc_alloc_chain(dev);
+ if (chain == NULL)
+ return -ENOMEM;
+
+ if (uvc_scan_chain_entity(chain, oterm) < 0)
+ goto error;
+
+ prev = oterm;
+
+ /*
+ * Add all Processing and Extension Units with two pads. The order
+ * doesn't matter much, use reverse list traversal to connect units in
+ * UVC descriptor order as we build the chain from output to input. This
+ * leads to units appearing in the order meant by the manufacturer for
+ * the cameras known to require this heuristic.
+ */
+ list_for_each_entry_reverse(entity, &dev->entities, list) {
+ if (entity->type != UVC_VC_PROCESSING_UNIT &&
+ entity->type != UVC_VC_EXTENSION_UNIT)
+ continue;
+
+ if (entity->num_pads != 2)
+ continue;
+
+ if (uvc_scan_chain_entity(chain, entity) < 0)
+ goto error;
+
+ prev->baSourceID[0] = entity->id;
+ prev = entity;
+ }
+
+ if (uvc_scan_chain_entity(chain, iterm) < 0)
+ goto error;
+
+ prev->baSourceID[0] = iterm->id;
+
+ list_add_tail(&chain->list, &dev->chains);
+
+ uvc_trace(UVC_TRACE_PROBE,
+ "Found a video chain by fallback heuristic (%s).\n",
+ uvc_print_chain(chain));
+
+ return 0;
+
+error:
+ kfree(chain);
+ return -EINVAL;
+}
+
/*
* Scan the device for video chains and register video devices.
*
@@ -1617,15 +1725,10 @@ static int uvc_scan_device(struct uvc_device *dev)
if (term->chain.next || term->chain.prev)
continue;
- chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+ chain = uvc_alloc_chain(dev);
if (chain == NULL)
return -ENOMEM;
- INIT_LIST_HEAD(&chain->entities);
- mutex_init(&chain->ctrl_mutex);
- chain->dev = dev;
- v4l2_prio_init(&chain->prio);
-
term->flags |= UVC_ENTITY_FLAG_DEFAULT;
if (uvc_scan_chain(chain, term) < 0) {
@@ -1639,6 +1742,9 @@ static int uvc_scan_device(struct uvc_device *dev)
list_add_tail(&chain->list, &dev->chains);
}
+ if (list_empty(&dev->chains))
+ uvc_scan_fallback(dev);
+
if (list_empty(&dev->chains)) {
uvc_printk(KERN_INFO, "No valid video chain found.\n");
return -1;
diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c
index e8ba1495de2b..27249eeec013 100644
--- a/drivers/mfd/wcd934x-regmap.c
+++ b/drivers/mfd/wcd934x-regmap.c
@@ -1937,7 +1937,6 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
case WCD934X_BIAS_VBG_FINE_ADJ:
case WCD934X_CODEC_CPR_SVS_CX_VDD:
case WCD934X_CODEC_CPR_SVS2_CX_VDD:
- case WCD934X_CDC_TOP_TOP_CFG1:
case WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL:
return true;
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 12ffa4192605..2a4abf736d89 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -549,11 +549,13 @@ config VEXPRESS_SYSCFG
bus. System Configuration interface is one of the possible means
of generating transactions on this bus.
-config UID_CPUTIME
- bool "Per-UID cpu time statistics"
+config UID_SYS_STATS
+ bool "Per-UID statistics"
depends on PROFILING
help
Per UID based cpu time statistics exported to /proc/uid_cputime
+ Per UID based io statistics exported to /proc/uid_io
+ Per UID based procstat control in /proc/uid_procstat
config QPNP_MISC
tristate "QPNP Misc Peripheral"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c2941afc961e..b0718228d2d9 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -62,7 +62,7 @@ endif
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
-obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o
+obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o
obj-y += qcom/
obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o
obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o
diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
index 9889d9c4723b..84761747f129 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
@@ -148,6 +148,8 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
case AUDIO_START: {
pr_debug("%s: AUDIO_START\n", __func__);
+ mutex_lock(&effects->lock);
+
rc = q6asm_open_read_write_v2(effects->ac,
FORMAT_LINEAR_PCM,
FORMAT_MULTI_CHANNEL_LINEAR_PCM,
@@ -159,6 +161,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
pr_err("%s: Open failed for hw accelerated effects:rc=%d\n",
__func__, rc);
rc = -EINVAL;
+ mutex_unlock(&effects->lock);
goto ioctl_fail;
}
effects->opened = 1;
@@ -175,6 +178,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
pr_err("%s: Write buffer Allocation failed rc = %d\n",
__func__, rc);
rc = -ENOMEM;
+ mutex_unlock(&effects->lock);
goto ioctl_fail;
}
atomic_set(&effects->in_count, effects->config.input.num_buf);
@@ -185,6 +189,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
pr_err("%s: Read buffer Allocation failed rc = %d\n",
__func__, rc);
rc = -ENOMEM;
+ mutex_unlock(&effects->lock);
goto readbuf_fail;
}
atomic_set(&effects->out_count, effects->config.output.num_buf);
@@ -199,6 +204,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
if (rc < 0) {
pr_err("%s: pcm read block config failed\n", __func__);
rc = -EINVAL;
+ mutex_unlock(&effects->lock);
goto cfg_fail;
}
pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n",
@@ -213,6 +219,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
pr_err("%s: pcm write format block config failed\n",
__func__);
rc = -EINVAL;
+ mutex_unlock(&effects->lock);
goto cfg_fail;
}
@@ -225,6 +232,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
effects->started = 0;
pr_err("%s: ASM run state failed\n", __func__);
}
+ mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_WRITE: {
@@ -286,8 +294,11 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
uint32_t idx = 0;
uint32_t size = 0;
+ mutex_lock(&effects->lock);
+
if (!effects->started) {
rc = -EFAULT;
+ mutex_unlock(&effects->lock);
goto ioctl_fail;
}
@@ -304,11 +315,13 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
if (!rc) {
pr_err("%s: read wait_event_timeout\n", __func__);
rc = -EFAULT;
+ mutex_unlock(&effects->lock);
goto ioctl_fail;
}
if (!atomic_read(&effects->in_count)) {
pr_err("%s: pcm stopped in_count 0\n", __func__);
rc = -EFAULT;
+ mutex_unlock(&effects->lock);
goto ioctl_fail;
}
@@ -316,15 +329,18 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
if (bufptr) {
if (!((void *)arg)) {
rc = -EFAULT;
+ mutex_unlock(&effects->lock);
goto ioctl_fail;
}
if ((effects->config.buf_cfg.input_len > size) ||
copy_to_user((void *)arg, bufptr,
effects->config.buf_cfg.input_len)) {
rc = -EFAULT;
+ mutex_unlock(&effects->lock);
goto ioctl_fail;
}
}
+ mutex_unlock(&effects->lock);
break;
}
default:
@@ -456,6 +472,7 @@ static long audio_effects_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case AUDIO_SET_EFFECTS_CONFIG: {
pr_debug("%s: AUDIO_SET_EFFECTS_CONFIG\n", __func__);
+ mutex_lock(&effects->lock);
memset(&effects->config, 0, sizeof(effects->config));
if (copy_from_user(&effects->config, (void *)arg,
sizeof(effects->config))) {
@@ -473,6 +490,7 @@ static long audio_effects_ioctl(struct file *file, unsigned int cmd,
effects->config.input.num_buf,
effects->config.input.sample_rate,
effects->config.input.num_channels);
+ mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_BUF_LEN: {
@@ -494,6 +512,7 @@ static long audio_effects_ioctl(struct file *file, unsigned int cmd,
buf_avail.input_num_avail = atomic_read(&effects->in_count);
buf_avail.output_num_avail = atomic_read(&effects->out_count);
+ mutex_lock(&effects->lock);
pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
__func__, buf_avail.output_num_avail,
buf_avail.input_num_avail);
@@ -503,16 +522,20 @@ static long audio_effects_ioctl(struct file *file, unsigned int cmd,
__func__);
rc = -EFAULT;
}
+ mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_PP_PARAMS: {
+ mutex_lock(&effects->lock);
if (copy_from_user(argvalues, (void *)arg,
MAX_PP_PARAMS_SZ*sizeof(long))) {
pr_err("%s: copy from user for pp params failed\n",
__func__);
+ mutex_unlock(&effects->lock);
return -EFAULT;
}
rc = audio_effects_set_pp_param(effects, argvalues);
+ mutex_unlock(&effects->lock);
break;
}
default:
@@ -578,12 +601,14 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
case AUDIO_SET_EFFECTS_CONFIG32: {
struct msm_hwacc_effects_config32 config32;
struct msm_hwacc_effects_config *config = &effects->config;
+ mutex_lock(&effects->lock);
memset(&effects->config, 0, sizeof(effects->config));
if (copy_from_user(&config32, (void *)arg,
sizeof(config32))) {
pr_err("%s: copy to user for AUDIO_SET_EFFECTS_CONFIG failed\n",
__func__);
rc = -EFAULT;
+ mutex_unlock(&effects->lock);
break;
}
config->input.buf_size = config32.input.buf_size;
@@ -620,16 +645,19 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
effects->config.input.num_buf,
effects->config.input.sample_rate,
effects->config.input.num_channels);
+ mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_BUF_LEN32: {
struct msm_hwacc_buf_cfg32 buf_cfg32;
struct msm_hwacc_effects_config *config = &effects->config;
+ mutex_lock(&effects->lock);
if (copy_from_user(&buf_cfg32, (void *)arg,
sizeof(buf_cfg32))) {
pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
__func__);
rc = -EFAULT;
+ mutex_unlock(&effects->lock);
break;
}
config->buf_cfg.input_len = buf_cfg32.input_len;
@@ -637,6 +665,7 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
pr_debug("%s: write buf len: %d, read buf len: %d\n",
__func__, effects->config.buf_cfg.output_len,
effects->config.buf_cfg.input_len);
+ mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_GET_BUF_AVAIL32: {
@@ -644,6 +673,7 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
memset(&buf_avail, 0, sizeof(buf_avail));
+ mutex_lock(&effects->lock);
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",
@@ -655,22 +685,26 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
__func__);
rc = -EFAULT;
}
+ mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_PP_PARAMS32: {
long argvalues[MAX_PP_PARAMS_SZ] = {0};
int argvalues32[MAX_PP_PARAMS_SZ] = {0};
+ mutex_lock(&effects->lock);
if (copy_from_user(argvalues32, (void *)arg,
MAX_PP_PARAMS_SZ*sizeof(int))) {
pr_err("%s: copy from user failed for pp params\n",
__func__);
+ mutex_unlock(&effects->lock);
return -EFAULT;
}
for (i = 0; i < MAX_PP_PARAMS_SZ; i++)
argvalues[i] = argvalues32[i];
rc = audio_effects_set_pp_param(effects, argvalues);
+ mutex_unlock(&effects->lock);
break;
}
case AUDIO_START32: {
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
index 3bb95f50bc13..52f7d3d2f268 100644
--- a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -179,7 +179,7 @@ static const int s_button_map[] = {
};
/* The opened devices container */
-static int s_opened_devs[MAX_DEVS_NUMBER];
+static atomic_t s_opened_devs[MAX_DEVS_NUMBER];
static struct wakeup_source usf_wakeup_source;
@@ -2338,14 +2338,11 @@ static uint16_t add_opened_dev(int minor)
uint16_t ind = 0;
for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) {
- if (minor == s_opened_devs[ind]) {
+ if (minor == atomic_cmpxchg(&s_opened_devs[ind], 0, minor)) {
pr_err("%s: device %d is already opened\n",
__func__, minor);
return USF_UNDEF_DEV_ID;
- }
-
- if (s_opened_devs[ind] == 0) {
- s_opened_devs[ind] = minor;
+ } else {
pr_debug("%s: device %d is added; ind=%d\n",
__func__, minor, ind);
return ind;
@@ -2401,7 +2398,7 @@ static int usf_release(struct inode *inode, struct file *file)
usf_disable(&usf->usf_tx);
usf_disable(&usf->usf_rx);
- s_opened_devs[usf->dev_ind] = 0;
+ atomic_set(&s_opened_devs[usf->dev_ind], 0);
wakeup_source_trash(&usf_wakeup_source);
mutex_unlock(&usf->mutex);
diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c
deleted file mode 100644
index c1ad5246f564..000000000000
--- a/drivers/misc/uid_cputime.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/* drivers/misc/uid_cputime.c
- *
- * Copyright (C) 2014 - 2015 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/hashtable.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/proc_fs.h>
-#include <linux/profile.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#define UID_HASH_BITS 10
-DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
-
-static DEFINE_MUTEX(uid_lock);
-static struct proc_dir_entry *parent;
-
-struct uid_entry {
- uid_t uid;
- cputime_t utime;
- cputime_t stime;
- cputime_t active_utime;
- cputime_t active_stime;
- struct hlist_node hash;
-};
-
-static struct uid_entry *find_uid_entry(uid_t uid)
-{
- struct uid_entry *uid_entry;
- hash_for_each_possible(hash_table, uid_entry, hash, uid) {
- if (uid_entry->uid == uid)
- return uid_entry;
- }
- return NULL;
-}
-
-static struct uid_entry *find_or_register_uid(uid_t uid)
-{
- struct uid_entry *uid_entry;
-
- uid_entry = find_uid_entry(uid);
- if (uid_entry)
- return uid_entry;
-
- uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
- if (!uid_entry)
- return NULL;
-
- uid_entry->uid = uid;
-
- hash_add(hash_table, &uid_entry->hash, uid);
-
- return uid_entry;
-}
-
-static int uid_stat_show(struct seq_file *m, void *v)
-{
- struct uid_entry *uid_entry;
- struct task_struct *task, *temp;
- cputime_t utime;
- cputime_t stime;
- unsigned long bkt;
-
- mutex_lock(&uid_lock);
-
- hash_for_each(hash_table, bkt, uid_entry, hash) {
- uid_entry->active_stime = 0;
- uid_entry->active_utime = 0;
- }
-
- read_lock(&tasklist_lock);
- do_each_thread(temp, task) {
- uid_entry = find_or_register_uid(from_kuid_munged(
- current_user_ns(), task_uid(task)));
- if (!uid_entry) {
- read_unlock(&tasklist_lock);
- mutex_unlock(&uid_lock);
- pr_err("%s: failed to find the uid_entry for uid %d\n",
- __func__, from_kuid_munged(current_user_ns(),
- task_uid(task)));
- return -ENOMEM;
- }
- task_cputime_adjusted(task, &utime, &stime);
- uid_entry->active_utime += utime;
- uid_entry->active_stime += stime;
- } while_each_thread(temp, task);
- read_unlock(&tasklist_lock);
-
- hash_for_each(hash_table, bkt, uid_entry, hash) {
- cputime_t total_utime = uid_entry->utime +
- uid_entry->active_utime;
- cputime_t total_stime = uid_entry->stime +
- uid_entry->active_stime;
- seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
- (unsigned long long)jiffies_to_msecs(
- cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
- (unsigned long long)jiffies_to_msecs(
- cputime_to_jiffies(total_stime)) * USEC_PER_MSEC);
- }
-
- mutex_unlock(&uid_lock);
- return 0;
-}
-
-static int uid_stat_open(struct inode *inode, struct file *file)
-{
- return single_open(file, uid_stat_show, PDE_DATA(inode));
-}
-
-static const struct file_operations uid_stat_fops = {
- .open = uid_stat_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int uid_remove_open(struct inode *inode, struct file *file)
-{
- return single_open(file, NULL, NULL);
-}
-
-static ssize_t uid_remove_write(struct file *file,
- const char __user *buffer, size_t count, loff_t *ppos)
-{
- struct uid_entry *uid_entry;
- struct hlist_node *tmp;
- char uids[128];
- char *start_uid, *end_uid = NULL;
- long int uid_start = 0, uid_end = 0;
-
- if (count >= sizeof(uids))
- count = sizeof(uids) - 1;
-
- if (copy_from_user(uids, buffer, count))
- return -EFAULT;
-
- uids[count] = '\0';
- end_uid = uids;
- start_uid = strsep(&end_uid, "-");
-
- if (!start_uid || !end_uid)
- return -EINVAL;
-
- if (kstrtol(start_uid, 10, &uid_start) != 0 ||
- kstrtol(end_uid, 10, &uid_end) != 0) {
- return -EINVAL;
- }
- mutex_lock(&uid_lock);
-
- for (; uid_start <= uid_end; uid_start++) {
- hash_for_each_possible_safe(hash_table, uid_entry, tmp,
- hash, (uid_t)uid_start) {
- if (uid_start == uid_entry->uid) {
- hash_del(&uid_entry->hash);
- kfree(uid_entry);
- }
- }
- }
-
- mutex_unlock(&uid_lock);
- return count;
-}
-
-static const struct file_operations uid_remove_fops = {
- .open = uid_remove_open,
- .release = single_release,
- .write = uid_remove_write,
-};
-
-static int process_notifier(struct notifier_block *self,
- unsigned long cmd, void *v)
-{
- struct task_struct *task = v;
- struct uid_entry *uid_entry;
- cputime_t utime, stime;
- uid_t uid;
-
- if (!task)
- return NOTIFY_OK;
-
- mutex_lock(&uid_lock);
- uid = from_kuid_munged(current_user_ns(), task_uid(task));
- uid_entry = find_or_register_uid(uid);
- if (!uid_entry) {
- pr_err("%s: failed to find uid %d\n", __func__, uid);
- goto exit;
- }
-
- task_cputime_adjusted(task, &utime, &stime);
- uid_entry->utime += utime;
- uid_entry->stime += stime;
-
-exit:
- mutex_unlock(&uid_lock);
- return NOTIFY_OK;
-}
-
-static struct notifier_block process_notifier_block = {
- .notifier_call = process_notifier,
-};
-
-static int __init proc_uid_cputime_init(void)
-{
- hash_init(hash_table);
-
- parent = proc_mkdir("uid_cputime", NULL);
- if (!parent) {
- pr_err("%s: failed to create proc entry\n", __func__);
- return -ENOMEM;
- }
-
- proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops,
- NULL);
-
- proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops,
- NULL);
-
- profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
-
- return 0;
-}
-
-early_initcall(proc_uid_cputime_init);
diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c
new file mode 100644
index 000000000000..204b23484266
--- /dev/null
+++ b/drivers/misc/uid_sys_stats.c
@@ -0,0 +1,463 @@
+/* drivers/misc/uid_cputime.c
+ *
+ * Copyright (C) 2014 - 2015 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/hashtable.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/profile.h>
+#include <linux/rtmutex.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#define UID_HASH_BITS 10
+DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
+
+static DEFINE_RT_MUTEX(uid_lock);
+static struct proc_dir_entry *cpu_parent;
+static struct proc_dir_entry *io_parent;
+static struct proc_dir_entry *proc_parent;
+
+struct io_stats {
+ u64 read_bytes;
+ u64 write_bytes;
+ u64 rchar;
+ u64 wchar;
+ u64 fsync;
+};
+
+#define UID_STATE_FOREGROUND 0
+#define UID_STATE_BACKGROUND 1
+#define UID_STATE_BUCKET_SIZE 2
+
+#define UID_STATE_TOTAL_CURR 2
+#define UID_STATE_TOTAL_LAST 3
+#define UID_STATE_SIZE 4
+
+struct uid_entry {
+ uid_t uid;
+ cputime_t utime;
+ cputime_t stime;
+ cputime_t active_utime;
+ cputime_t active_stime;
+ int state;
+ struct io_stats io[UID_STATE_SIZE];
+ struct hlist_node hash;
+};
+
+static struct uid_entry *find_uid_entry(uid_t uid)
+{
+ struct uid_entry *uid_entry;
+ hash_for_each_possible(hash_table, uid_entry, hash, uid) {
+ if (uid_entry->uid == uid)
+ return uid_entry;
+ }
+ return NULL;
+}
+
+static struct uid_entry *find_or_register_uid(uid_t uid)
+{
+ struct uid_entry *uid_entry;
+
+ uid_entry = find_uid_entry(uid);
+ if (uid_entry)
+ return uid_entry;
+
+ uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
+ if (!uid_entry)
+ return NULL;
+
+ uid_entry->uid = uid;
+
+ hash_add(hash_table, &uid_entry->hash, uid);
+
+ return uid_entry;
+}
+
+static int uid_cputime_show(struct seq_file *m, void *v)
+{
+ struct uid_entry *uid_entry;
+ struct task_struct *task, *temp;
+ cputime_t utime;
+ cputime_t stime;
+ unsigned long bkt;
+
+ rt_mutex_lock(&uid_lock);
+
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
+ uid_entry->active_stime = 0;
+ uid_entry->active_utime = 0;
+ }
+
+ read_lock(&tasklist_lock);
+ do_each_thread(temp, task) {
+ uid_entry = find_or_register_uid(from_kuid_munged(
+ current_user_ns(), task_uid(task)));
+ if (!uid_entry) {
+ read_unlock(&tasklist_lock);
+ rt_mutex_unlock(&uid_lock);
+ pr_err("%s: failed to find the uid_entry for uid %d\n",
+ __func__, from_kuid_munged(current_user_ns(),
+ task_uid(task)));
+ return -ENOMEM;
+ }
+ task_cputime_adjusted(task, &utime, &stime);
+ uid_entry->active_utime += utime;
+ uid_entry->active_stime += stime;
+ } while_each_thread(temp, task);
+ read_unlock(&tasklist_lock);
+
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
+ cputime_t total_utime = uid_entry->utime +
+ uid_entry->active_utime;
+ cputime_t total_stime = uid_entry->stime +
+ uid_entry->active_stime;
+ seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
+ (unsigned long long)jiffies_to_msecs(
+ cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
+ (unsigned long long)jiffies_to_msecs(
+ cputime_to_jiffies(total_stime)) * USEC_PER_MSEC);
+ }
+
+ rt_mutex_unlock(&uid_lock);
+ return 0;
+}
+
+static int uid_cputime_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, uid_cputime_show, PDE_DATA(inode));
+}
+
+static const struct file_operations uid_cputime_fops = {
+ .open = uid_cputime_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int uid_remove_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, NULL, NULL);
+}
+
+static ssize_t uid_remove_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct uid_entry *uid_entry;
+ struct hlist_node *tmp;
+ char uids[128];
+ char *start_uid, *end_uid = NULL;
+ long int uid_start = 0, uid_end = 0;
+
+ if (count >= sizeof(uids))
+ count = sizeof(uids) - 1;
+
+ if (copy_from_user(uids, buffer, count))
+ return -EFAULT;
+
+ uids[count] = '\0';
+ end_uid = uids;
+ start_uid = strsep(&end_uid, "-");
+
+ if (!start_uid || !end_uid)
+ return -EINVAL;
+
+ if (kstrtol(start_uid, 10, &uid_start) != 0 ||
+ kstrtol(end_uid, 10, &uid_end) != 0) {
+ return -EINVAL;
+ }
+ rt_mutex_lock(&uid_lock);
+
+ for (; uid_start <= uid_end; uid_start++) {
+ hash_for_each_possible_safe(hash_table, uid_entry, tmp,
+ hash, (uid_t)uid_start) {
+ if (uid_start == uid_entry->uid) {
+ hash_del(&uid_entry->hash);
+ kfree(uid_entry);
+ }
+ }
+ }
+
+ rt_mutex_unlock(&uid_lock);
+ return count;
+}
+
+static const struct file_operations uid_remove_fops = {
+ .open = uid_remove_open,
+ .release = single_release,
+ .write = uid_remove_write,
+};
+
+static u64 compute_write_bytes(struct task_struct *task)
+{
+ if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
+ return 0;
+
+ return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
+}
+
+static void add_uid_io_curr_stats(struct uid_entry *uid_entry,
+ struct task_struct *task)
+{
+ struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
+
+ io_curr->read_bytes += task->ioac.read_bytes;
+ io_curr->write_bytes += compute_write_bytes(task);
+ io_curr->rchar += task->ioac.rchar;
+ io_curr->wchar += task->ioac.wchar;
+ io_curr->fsync += task->ioac.syscfs;
+}
+
+static void clean_uid_io_last_stats(struct uid_entry *uid_entry,
+ struct task_struct *task)
+{
+ struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
+
+ io_last->read_bytes -= task->ioac.read_bytes;
+ io_last->write_bytes -= compute_write_bytes(task);
+ io_last->rchar -= task->ioac.rchar;
+ io_last->wchar -= task->ioac.wchar;
+ io_last->fsync -= task->ioac.syscfs;
+}
+
+static void update_io_stats_locked(void)
+{
+ struct uid_entry *uid_entry;
+ struct task_struct *task, *temp;
+ struct io_stats *io_bucket, *io_curr, *io_last;
+ unsigned long bkt;
+
+ BUG_ON(!rt_mutex_is_locked(&uid_lock));
+
+ hash_for_each(hash_table, bkt, uid_entry, hash)
+ memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
+ sizeof(struct io_stats));
+
+ read_lock(&tasklist_lock);
+ do_each_thread(temp, task) {
+ uid_entry = find_or_register_uid(from_kuid_munged(
+ current_user_ns(), task_uid(task)));
+ if (!uid_entry)
+ continue;
+ add_uid_io_curr_stats(uid_entry, task);
+ } while_each_thread(temp, task);
+ read_unlock(&tasklist_lock);
+
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
+ io_bucket = &uid_entry->io[uid_entry->state];
+ io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
+ io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
+
+ io_bucket->read_bytes +=
+ io_curr->read_bytes - io_last->read_bytes;
+ io_bucket->write_bytes +=
+ io_curr->write_bytes - io_last->write_bytes;
+ io_bucket->rchar += io_curr->rchar - io_last->rchar;
+ io_bucket->wchar += io_curr->wchar - io_last->wchar;
+ io_bucket->fsync += io_curr->fsync - io_last->fsync;
+
+ io_last->read_bytes = io_curr->read_bytes;
+ io_last->write_bytes = io_curr->write_bytes;
+ io_last->rchar = io_curr->rchar;
+ io_last->wchar = io_curr->wchar;
+ io_last->fsync = io_curr->fsync;
+ }
+}
+
+static int uid_io_show(struct seq_file *m, void *v)
+{
+ struct uid_entry *uid_entry;
+ unsigned long bkt;
+
+ rt_mutex_lock(&uid_lock);
+
+ update_io_stats_locked();
+
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
+ seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
+ uid_entry->uid,
+ uid_entry->io[UID_STATE_FOREGROUND].rchar,
+ uid_entry->io[UID_STATE_FOREGROUND].wchar,
+ uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
+ uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
+ uid_entry->io[UID_STATE_BACKGROUND].rchar,
+ uid_entry->io[UID_STATE_BACKGROUND].wchar,
+ uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
+ uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
+ uid_entry->io[UID_STATE_FOREGROUND].fsync,
+ uid_entry->io[UID_STATE_BACKGROUND].fsync);
+ }
+
+ rt_mutex_unlock(&uid_lock);
+
+ return 0;
+}
+
+static int uid_io_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, uid_io_show, PDE_DATA(inode));
+}
+
+static const struct file_operations uid_io_fops = {
+ .open = uid_io_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int uid_procstat_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, NULL, NULL);
+}
+
+static ssize_t uid_procstat_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct uid_entry *uid_entry;
+ uid_t uid;
+ int argc, state;
+ char input[128];
+
+ if (count >= sizeof(input))
+ return -EINVAL;
+
+ if (copy_from_user(input, buffer, count))
+ return -EFAULT;
+
+ input[count] = '\0';
+
+ argc = sscanf(input, "%u %d", &uid, &state);
+ if (argc != 2)
+ return -EINVAL;
+
+ if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
+ return -EINVAL;
+
+ rt_mutex_lock(&uid_lock);
+
+ uid_entry = find_or_register_uid(uid);
+ if (!uid_entry) {
+ rt_mutex_unlock(&uid_lock);
+ return -EINVAL;
+ }
+
+ if (uid_entry->state == state) {
+ rt_mutex_unlock(&uid_lock);
+ return count;
+ }
+
+ update_io_stats_locked();
+
+ uid_entry->state = state;
+
+ rt_mutex_unlock(&uid_lock);
+
+ return count;
+}
+
+static const struct file_operations uid_procstat_fops = {
+ .open = uid_procstat_open,
+ .release = single_release,
+ .write = uid_procstat_write,
+};
+
+static int process_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ struct task_struct *task = v;
+ struct uid_entry *uid_entry;
+ cputime_t utime, stime;
+ uid_t uid;
+
+ if (!task)
+ return NOTIFY_OK;
+
+ rt_mutex_lock(&uid_lock);
+ uid = from_kuid_munged(current_user_ns(), task_uid(task));
+ uid_entry = find_or_register_uid(uid);
+ if (!uid_entry) {
+ pr_err("%s: failed to find uid %d\n", __func__, uid);
+ goto exit;
+ }
+
+ task_cputime_adjusted(task, &utime, &stime);
+ uid_entry->utime += utime;
+ uid_entry->stime += stime;
+
+ update_io_stats_locked();
+ clean_uid_io_last_stats(uid_entry, task);
+
+exit:
+ rt_mutex_unlock(&uid_lock);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block process_notifier_block = {
+ .notifier_call = process_notifier,
+};
+
+static int __init proc_uid_sys_stats_init(void)
+{
+ hash_init(hash_table);
+
+ cpu_parent = proc_mkdir("uid_cputime", NULL);
+ if (!cpu_parent) {
+ pr_err("%s: failed to create uid_cputime proc entry\n",
+ __func__);
+ goto err;
+ }
+
+ proc_create_data("remove_uid_range", 0222, cpu_parent,
+ &uid_remove_fops, NULL);
+ proc_create_data("show_uid_stat", 0444, cpu_parent,
+ &uid_cputime_fops, NULL);
+
+ io_parent = proc_mkdir("uid_io", NULL);
+ if (!io_parent) {
+ pr_err("%s: failed to create uid_io proc entry\n",
+ __func__);
+ goto err;
+ }
+
+ proc_create_data("stats", 0444, io_parent,
+ &uid_io_fops, NULL);
+
+ proc_parent = proc_mkdir("uid_procstat", NULL);
+ if (!proc_parent) {
+ pr_err("%s: failed to create uid_procstat proc entry\n",
+ __func__);
+ goto err;
+ }
+
+ proc_create_data("set", 0222, proc_parent,
+ &uid_procstat_fops, NULL);
+
+ profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
+
+ return 0;
+
+err:
+ remove_proc_subtree("uid_cputime", NULL);
+ remove_proc_subtree("uid_io", NULL);
+ remove_proc_subtree("uid_procstat", NULL);
+ return -ENOMEM;
+}
+
+early_initcall(proc_uid_sys_stats_init);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3b79f514350e..afdf70000651 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -680,6 +680,12 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.ffu_capable =
(ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) &&
!(ext_csd[EXT_CSD_FW_CONFIG] & 0x1);
+
+ card->ext_csd.pre_eol_info = ext_csd[EXT_CSD_PRE_EOL_INFO];
+ card->ext_csd.device_life_time_est_typ_a =
+ ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A];
+ card->ext_csd.device_life_time_est_typ_b =
+ ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B];
}
out:
return err;
@@ -813,6 +819,11 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv);
+MMC_DEV_ATTR(rev, "0x%x\n", card->ext_csd.rev);
+MMC_DEV_ATTR(pre_eol_info, "%02x\n", card->ext_csd.pre_eol_info);
+MMC_DEV_ATTR(life_time, "0x%02x 0x%02x\n",
+ card->ext_csd.device_life_time_est_typ_a,
+ card->ext_csd.device_life_time_est_typ_b);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
card->ext_csd.enhanced_area_offset);
@@ -851,6 +862,9 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_name.attr,
&dev_attr_oemid.attr,
&dev_attr_prv.attr,
+ &dev_attr_rev.attr,
+ &dev_attr_pre_eol_info.attr,
+ &dev_attr_life_time.attr,
&dev_attr_serial.attr,
&dev_attr_enhanced_area_offset.attr,
&dev_attr_enhanced_area_size.attr,
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c
index d2c386f09d69..1d843357422e 100644
--- a/drivers/mmc/host/ushc.c
+++ b/drivers/mmc/host/ushc.c
@@ -426,6 +426,9 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id
struct ushc_data *ushc;
int ret;
+ if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev);
if (mmc == NULL)
return -ENOMEM;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
index b6fa89102526..66ba1e0ff37e 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
@@ -913,8 +913,8 @@
#define RX_PACKET_ATTRIBUTES_CSUM_DONE_WIDTH 1
#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX 1
#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1
-#define RX_PACKET_ATTRIBUTES_INCOMPLETE_INDEX 2
-#define RX_PACKET_ATTRIBUTES_INCOMPLETE_WIDTH 1
+#define RX_PACKET_ATTRIBUTES_LAST_INDEX 2
+#define RX_PACKET_ATTRIBUTES_LAST_WIDTH 1
#define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_INDEX 3
#define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_WIDTH 1
#define RX_PACKET_ATTRIBUTES_CONTEXT_INDEX 4
@@ -923,6 +923,8 @@
#define RX_PACKET_ATTRIBUTES_RX_TSTAMP_WIDTH 1
#define RX_PACKET_ATTRIBUTES_RSS_HASH_INDEX 6
#define RX_PACKET_ATTRIBUTES_RSS_HASH_WIDTH 1
+#define RX_PACKET_ATTRIBUTES_FIRST_INDEX 7
+#define RX_PACKET_ATTRIBUTES_FIRST_WIDTH 1
#define RX_NORMAL_DESC0_OVT_INDEX 0
#define RX_NORMAL_DESC0_OVT_WIDTH 16
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
index f6a7161e3b85..5e6238e0b2bd 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
@@ -1658,10 +1658,15 @@ static int xgbe_dev_read(struct xgbe_channel *channel)
/* Get the header length */
if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, FD)) {
+ XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+ FIRST, 1);
rdata->rx.hdr_len = XGMAC_GET_BITS_LE(rdesc->desc2,
RX_NORMAL_DESC2, HL);
if (rdata->rx.hdr_len)
pdata->ext_stats.rx_split_header_packets++;
+ } else {
+ XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+ FIRST, 0);
}
/* Get the RSS hash */
@@ -1684,19 +1689,16 @@ static int xgbe_dev_read(struct xgbe_channel *channel)
}
}
- /* Get the packet length */
- rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL);
-
- if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD)) {
- /* Not all the data has been transferred for this packet */
- XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
- INCOMPLETE, 1);
+ /* Not all the data has been transferred for this packet */
+ if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD))
return 0;
- }
/* This is the last of the data for this packet */
XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
- INCOMPLETE, 0);
+ LAST, 1);
+
+ /* Get the packet length */
+ rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL);
/* Set checksum done indicator as appropriate */
if (netdev->features & NETIF_F_RXCSUM)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 53ce1222b11d..865b7e0b133b 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -1760,13 +1760,12 @@ static struct sk_buff *xgbe_create_skb(struct xgbe_prv_data *pdata,
{
struct sk_buff *skb;
u8 *packet;
- unsigned int copy_len;
skb = napi_alloc_skb(napi, rdata->rx.hdr.dma_len);
if (!skb)
return NULL;
- /* Start with the header buffer which may contain just the header
+ /* Pull in the header buffer which may contain just the header
* or the header plus data
*/
dma_sync_single_range_for_cpu(pdata->dev, rdata->rx.hdr.dma_base,
@@ -1775,30 +1774,49 @@ static struct sk_buff *xgbe_create_skb(struct xgbe_prv_data *pdata,
packet = page_address(rdata->rx.hdr.pa.pages) +
rdata->rx.hdr.pa.pages_offset;
- copy_len = (rdata->rx.hdr_len) ? rdata->rx.hdr_len : len;
- copy_len = min(rdata->rx.hdr.dma_len, copy_len);
- skb_copy_to_linear_data(skb, packet, copy_len);
- skb_put(skb, copy_len);
-
- len -= copy_len;
- if (len) {
- /* Add the remaining data as a frag */
- dma_sync_single_range_for_cpu(pdata->dev,
- rdata->rx.buf.dma_base,
- rdata->rx.buf.dma_off,
- rdata->rx.buf.dma_len,
- DMA_FROM_DEVICE);
-
- skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- rdata->rx.buf.pa.pages,
- rdata->rx.buf.pa.pages_offset,
- len, rdata->rx.buf.dma_len);
- rdata->rx.buf.pa.pages = NULL;
- }
+ skb_copy_to_linear_data(skb, packet, len);
+ skb_put(skb, len);
return skb;
}
+static unsigned int xgbe_rx_buf1_len(struct xgbe_ring_data *rdata,
+ struct xgbe_packet_data *packet)
+{
+ /* Always zero if not the first descriptor */
+ if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, FIRST))
+ return 0;
+
+ /* First descriptor with split header, return header length */
+ if (rdata->rx.hdr_len)
+ return rdata->rx.hdr_len;
+
+ /* First descriptor but not the last descriptor and no split header,
+ * so the full buffer was used
+ */
+ if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, LAST))
+ return rdata->rx.hdr.dma_len;
+
+ /* First descriptor and last descriptor and no split header, so
+ * calculate how much of the buffer was used
+ */
+ return min_t(unsigned int, rdata->rx.hdr.dma_len, rdata->rx.len);
+}
+
+static unsigned int xgbe_rx_buf2_len(struct xgbe_ring_data *rdata,
+ struct xgbe_packet_data *packet,
+ unsigned int len)
+{
+ /* Always the full buffer if not the last descriptor */
+ if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, LAST))
+ return rdata->rx.buf.dma_len;
+
+ /* Last descriptor so calculate how much of the buffer was used
+ * for the last bit of data
+ */
+ return rdata->rx.len - len;
+}
+
static int xgbe_tx_poll(struct xgbe_channel *channel)
{
struct xgbe_prv_data *pdata = channel->pdata;
@@ -1881,8 +1899,8 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget)
struct napi_struct *napi;
struct sk_buff *skb;
struct skb_shared_hwtstamps *hwtstamps;
- unsigned int incomplete, error, context_next, context;
- unsigned int len, rdesc_len, max_len;
+ unsigned int last, error, context_next, context;
+ unsigned int len, buf1_len, buf2_len, max_len;
unsigned int received = 0;
int packet_count = 0;
@@ -1892,7 +1910,7 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget)
if (!ring)
return 0;
- incomplete = 0;
+ last = 0;
context_next = 0;
napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi;
@@ -1926,9 +1944,8 @@ read_again:
received++;
ring->cur++;
- incomplete = XGMAC_GET_BITS(packet->attributes,
- RX_PACKET_ATTRIBUTES,
- INCOMPLETE);
+ last = XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+ LAST);
context_next = XGMAC_GET_BITS(packet->attributes,
RX_PACKET_ATTRIBUTES,
CONTEXT_NEXT);
@@ -1937,7 +1954,7 @@ read_again:
CONTEXT);
/* Earlier error, just drain the remaining data */
- if ((incomplete || context_next) && error)
+ if ((!last || context_next) && error)
goto read_again;
if (error || packet->errors) {
@@ -1949,16 +1966,22 @@ read_again:
}
if (!context) {
- /* Length is cumulative, get this descriptor's length */
- rdesc_len = rdata->rx.len - len;
- len += rdesc_len;
+ /* Get the data length in the descriptor buffers */
+ buf1_len = xgbe_rx_buf1_len(rdata, packet);
+ len += buf1_len;
+ buf2_len = xgbe_rx_buf2_len(rdata, packet, len);
+ len += buf2_len;
- if (rdesc_len && !skb) {
+ if (!skb) {
skb = xgbe_create_skb(pdata, napi, rdata,
- rdesc_len);
- if (!skb)
+ buf1_len);
+ if (!skb) {
error = 1;
- } else if (rdesc_len) {
+ goto skip_data;
+ }
+ }
+
+ if (buf2_len) {
dma_sync_single_range_for_cpu(pdata->dev,
rdata->rx.buf.dma_base,
rdata->rx.buf.dma_off,
@@ -1968,13 +1991,14 @@ read_again:
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
rdata->rx.buf.pa.pages,
rdata->rx.buf.pa.pages_offset,
- rdesc_len,
+ buf2_len,
rdata->rx.buf.dma_len);
rdata->rx.buf.pa.pages = NULL;
}
}
- if (incomplete || context_next)
+skip_data:
+ if (!last || context_next)
goto read_again;
if (!skb)
@@ -2033,7 +2057,7 @@ next_packet:
}
/* Check if we need to save state before leaving */
- if (received && (incomplete || context_next)) {
+ if (received && (!last || context_next)) {
rdata = XGBE_GET_DESC_DATA(ring, ring->cur);
rdata->state_saved = 1;
rdata->state.skb = skb;
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 91627561c58d..f971d92f7b41 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -3495,7 +3495,8 @@ static int bcmgenet_suspend(struct device *d)
bcmgenet_netif_stop(dev);
- phy_suspend(priv->phydev);
+ if (!device_may_wakeup(d))
+ phy_suspend(priv->phydev);
netif_device_detach(dev);
@@ -3592,7 +3593,8 @@ static int bcmgenet_resume(struct device *d)
netif_device_attach(dev);
- phy_resume(priv->phydev);
+ if (!device_may_wakeup(d))
+ phy_resume(priv->phydev);
if (priv->eee.eee_enabled)
bcmgenet_eee_enable_set(dev, true);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index 8bdfe53754ba..e96d1f95bb47 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -220,20 +220,6 @@ void bcmgenet_phy_power_set(struct net_device *dev, bool enable)
udelay(60);
}
-static void bcmgenet_internal_phy_setup(struct net_device *dev)
-{
- struct bcmgenet_priv *priv = netdev_priv(dev);
- u32 reg;
-
- /* Power up PHY */
- bcmgenet_phy_power_set(dev, true);
- /* enable APD */
- reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
- reg |= EXT_PWR_DN_EN_LD;
- bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
- bcmgenet_mii_reset(dev);
-}
-
static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
{
u32 reg;
@@ -281,7 +267,6 @@ int bcmgenet_mii_config(struct net_device *dev)
if (priv->internal_phy) {
phy_name = "internal PHY";
- bcmgenet_internal_phy_setup(dev);
} else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
phy_name = "MoCA";
bcmgenet_moca_phy_setup(priv);
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index 23ec28f43f6d..afaa98d1d4e4 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -77,6 +77,10 @@ s32 igb_get_phy_id(struct e1000_hw *hw)
s32 ret_val = 0;
u16 phy_id;
+ /* ensure PHY page selection to fix misconfigured i210 */
+ if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211))
+ phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0);
+
ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
if (ret_val)
goto out;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index cf0098596e85..e9408f5e2a1d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -197,6 +197,10 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
if (lro_num_seg > 1) {
mlx5e_lro_update_hdr(skb, cqe);
skb_shinfo(skb)->gso_size = DIV_ROUND_UP(cqe_bcnt, lro_num_seg);
+ /* Subtract one since we already counted this as one
+ * "regular" packet in mlx5e_complete_rx_cqe()
+ */
+ rq->stats.packets += lro_num_seg - 1;
rq->stats.lro_packets++;
rq->stats.lro_bytes += cqe_bcnt;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index ba115ec7aa92..1e611980cf99 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -85,7 +85,7 @@ static struct mlx5_profile profile[] = {
[2] = {
.mask = MLX5_PROF_MASK_QP_SIZE |
MLX5_PROF_MASK_MR_CACHE,
- .log_max_qp = 17,
+ .log_max_qp = 18,
.mr_cache[0] = {
.size = 500,
.limit = 250
diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
index 015cb99d445b..60b7a64c2edb 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
@@ -983,14 +983,17 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
if (cb_info->chan == rmnet_mhi_ptr->rx_channel) {
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
"Receive MHI_DISABLE notification for rx path\n");
- rmnet_mhi_disable(rmnet_mhi_ptr);
+ if (rmnet_mhi_ptr->dev)
+ rmnet_mhi_disable(rmnet_mhi_ptr);
} else {
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
"Receive MHI_DISABLE notification for tx path\n");
rmnet_mhi_ptr->tx_enabled = 0;
- rmnet_mhi_internal_clean_unmap_buffers
- (rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->tx_buffers,
- DMA_TO_DEVICE);
+ if (rmnet_mhi_ptr->dev)
+ rmnet_mhi_internal_clean_unmap_buffers(
+ rmnet_mhi_ptr->dev,
+ &rmnet_mhi_ptr->tx_buffers,
+ DMA_TO_DEVICE);
}
/* Remove all votes disabling low power mode */
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index e8a09ff9e724..c8a7802d2953 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -197,65 +197,6 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size,
return ppi;
}
-union sub_key {
- u64 k;
- struct {
- u8 pad[3];
- u8 kb;
- u32 ka;
- };
-};
-
-/* Toeplitz hash function
- * data: network byte order
- * return: host byte order
- */
-static u32 comp_hash(u8 *key, int klen, void *data, int dlen)
-{
- union sub_key subk;
- int k_next = 4;
- u8 dt;
- int i, j;
- u32 ret = 0;
-
- subk.k = 0;
- subk.ka = ntohl(*(u32 *)key);
-
- for (i = 0; i < dlen; i++) {
- subk.kb = key[k_next];
- k_next = (k_next + 1) % klen;
- dt = ((u8 *)data)[i];
- for (j = 0; j < 8; j++) {
- if (dt & 0x80)
- ret ^= subk.ka;
- dt <<= 1;
- subk.k <<= 1;
- }
- }
-
- return ret;
-}
-
-static bool netvsc_set_hash(u32 *hash, struct sk_buff *skb)
-{
- struct flow_keys flow;
- int data_len;
-
- if (!skb_flow_dissect_flow_keys(skb, &flow, 0) ||
- !(flow.basic.n_proto == htons(ETH_P_IP) ||
- flow.basic.n_proto == htons(ETH_P_IPV6)))
- return false;
-
- if (flow.basic.ip_proto == IPPROTO_TCP)
- data_len = 12;
- else
- data_len = 8;
-
- *hash = comp_hash(netvsc_hash_key, HASH_KEYLEN, &flow, data_len);
-
- return true;
-}
-
static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
void *accel_priv, select_queue_fallback_t fallback)
{
@@ -268,11 +209,9 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1)
return 0;
- if (netvsc_set_hash(&hash, skb)) {
- q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] %
- ndev->real_num_tx_queues;
- skb_set_hash(skb, hash, PKT_HASH_TYPE_L3);
- }
+ hash = skb_get_hash(skb);
+ q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] %
+ ndev->real_num_tx_queues;
return q_idx;
}
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index d6b619667f1a..349aecbc210a 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -345,6 +345,7 @@ static netdev_tx_t is_ip_tx_frame(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ int len = skb->len;
netdev_tx_t ret = is_ip_tx_frame(skb, dev);
if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
@@ -352,7 +353,7 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev)
u64_stats_update_begin(&dstats->syncp);
dstats->tx_pkts++;
- dstats->tx_bytes += skb->len;
+ dstats->tx_bytes += len;
u64_stats_update_end(&dstats->syncp);
} else {
this_cpu_inc(dev->dstats->tx_drps);
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 6fa8e165878e..590750ab6564 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -2600,7 +2600,7 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[])
if (data[IFLA_VXLAN_ID]) {
__u32 id = nla_get_u32(data[IFLA_VXLAN_ID]);
- if (id >= VXLAN_VID_MASK)
+ if (id >= VXLAN_N_VID)
return -ERANGE;
}
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 6dd396430f19..b0d6c2614731 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -122,6 +122,19 @@ struct htt_msdu_ext_desc {
| HTT_MSDU_EXT_DESC_FLAG_TCP_IPV4_CSUM_ENABLE \
| HTT_MSDU_EXT_DESC_FLAG_TCP_IPV6_CSUM_ENABLE)
+#define HTT_TX_IPV4_CSUM_EN BIT(16)
+#define HTT_TX_UDP_IPV4_CSUM_EN BIT(17)
+#define HTT_TX_UDP_IPV6_CSUM_EN BIT(18)
+#define HTT_TX_TCP_IPV4_CSUM_EN BIT(19)
+#define HTT_TX_TCP_IPV6_CSUM_EN BIT(20)
+#define HTT_TX_PARTIAL_CSUM_EN BIT(21)
+
+#define HTT_TX_CHECKSUM_ENABLE (HTT_TX_IPV4_CSUM_EN \
+ | HTT_TX_UDP_IPV4_CSUM_EN \
+ | HTT_TX_UDP_IPV6_CSUM_EN \
+ | HTT_TX_TCP_IPV4_CSUM_EN \
+ | HTT_TX_TCP_IPV6_CSUM_EN)
+
enum htt_data_tx_desc_flags0 {
HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0,
HTT_DATA_TX_DESC_FLAGS0_NO_AGGR = 1 << 1,
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 62e7f5e4b6fc..1b59721a91ac 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -1051,8 +1051,14 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
- if (ar->hw_params.continuous_frag_desc)
+ if (ar->hw_params.continuous_frag_desc) {
ext_desc->flags |= HTT_MSDU_CHECKSUM_ENABLE;
+ if (QCA_REV_WCN3990(ar)) {
+ memset(ext_desc->tso_flag, 0,
+ sizeof(ext_desc->tso_flag));
+ ext_desc->tso_flag[3] |= HTT_TX_CHECKSUM_ENABLE;
+ }
+ }
}
/* Prevent firmware from sending up tx inspection requests. There's
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 35e5d980ed49..b9d08b4b4cc5 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -4671,7 +4671,8 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar)
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
- WARN_ON(arvif->txpower < 0);
+ if (arvif->txpower <= 0)
+ continue;
if (txpower == -1)
txpower = arvif->txpower;
@@ -4679,8 +4680,8 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar)
txpower = min(txpower, arvif->txpower);
}
- if (WARN_ON(txpower == -1))
- return -EINVAL;
+ if (txpower == -1)
+ return 0;
ret = ath10k_mac_txpower_setup(ar, txpower);
if (ret) {
@@ -5190,6 +5191,10 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
ath10k_warn(ar, "failed to recalc monitor: %d\n", ret);
}
+ ret = ath10k_mac_txpower_recalc(ar);
+ if (ret)
+ ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
+
spin_lock_bh(&ar->htt.tx_lock);
ath10k_mac_vif_tx_unlock_all(arvif);
spin_unlock_bh(&ar->htt.tx_lock);
@@ -7993,10 +7998,8 @@ int ath10k_mac_register(struct ath10k *ar)
goto err_free;
}
- if (!QCA_REV_WCN3990(ar)) {
- if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
- ar->hw->netdev_features = NETIF_F_HW_CSUM;
- }
+ if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
+ ar->hw->netdev_features = NETIF_F_HW_CSUM;
if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) {
/* Init ath dfs pattern detector */
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
index f172671cb00f..84a9b1a9577c 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.c
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -854,7 +854,6 @@ int ath10k_snoc_start_qmi_service(struct ath10k *ar)
goto out_destroy_wq;
}
- atomic_set(&qmi_cfg->fw_ready, 1);
ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI service started successfully\n");
return 0;
diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h
index c8bc26bb96b2..29ad5acdf414 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.h
+++ b/drivers/net/wireless/ath/ath10k/qmi.h
@@ -18,7 +18,7 @@
#define ATH10K_SNOC_WLAN_FW_READY_TIMEOUT 8000
#define WLFW_SERVICE_INS_ID_V01 0
-#define WLFW_CLIENT_ID 0x4b4e454c
+#define WLFW_CLIENT_ID 0x41544851
#define WLFW_TIMEOUT_MS 20000
enum ath10k_snoc_driver_event_type {
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index add0a7cd9edb..2cbc8ee9abf9 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -650,6 +650,9 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
if (!ar_snoc)
return -EINVAL;
+ if (atomic_read(&ar_snoc->fw_crashed))
+ return -ESHUTDOWN;
+
snoc_pipe = &ar_snoc->pipe_info[pipe_id];
ce_pipe = snoc_pipe->ce_hdl;
src_ring = ce_pipe->src_ring;
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 476521b77008..37898146f01d 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1414,6 +1414,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
wil_set_recovery_state(wil, fw_recovery_idle);
+ set_bit(wil_status_resetting, wil->status);
+
mutex_lock(&wil->mutex);
wmi_pcp_stop(wil);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index f82bb8964868..fca8acffeed5 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -190,6 +190,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
break;
}
sta->status = wil_sta_unused;
+ sta->fst_link_loss = false;
}
/* reorder buffers */
for (i = 0; i < WIL_STA_TID_NUM; i++) {
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index d472e13fb9d9..2a515e848820 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -26,6 +26,10 @@ static bool use_msi = true;
module_param(use_msi, bool, 0444);
MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
+static bool ftm_mode;
+module_param(ftm_mode, bool, 0444);
+MODULE_PARM_DESC(ftm_mode, " Set factory test mode, default - false");
+
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int wil6210_pm_notify(struct notifier_block *notify_block,
@@ -36,13 +40,15 @@ static int wil6210_pm_notify(struct notifier_block *notify_block,
static
void wil_set_capabilities(struct wil6210_priv *wil)
{
+ const char *wil_fw_name;
u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
RGF_USER_REVISION_ID_MASK);
bitmap_zero(wil->hw_capabilities, hw_capability_last);
bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
- wil->wil_fw_name = WIL_FW_NAME_DEFAULT;
+ wil->wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_DEFAULT :
+ WIL_FW_NAME_DEFAULT;
wil->chip_revision = chip_revision;
switch (jtag_id) {
@@ -51,9 +57,11 @@ void wil_set_capabilities(struct wil6210_priv *wil)
case REVISION_ID_SPARROW_D0:
wil->hw_name = "Sparrow D0";
wil->hw_version = HW_VER_SPARROW_D0;
- if (wil_fw_verify_file_exists(wil,
- WIL_FW_NAME_SPARROW_PLUS))
- wil->wil_fw_name = WIL_FW_NAME_SPARROW_PLUS;
+ wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_SPARROW_PLUS :
+ WIL_FW_NAME_SPARROW_PLUS;
+
+ if (wil_fw_verify_file_exists(wil, wil_fw_name))
+ wil->wil_fw_name = wil_fw_name;
break;
case REVISION_ID_SPARROW_B0:
wil->hw_name = "Sparrow B0";
diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c
index a3689738a070..b4c4d09011b4 100644
--- a/drivers/net/wireless/ath/wil6210/sysfs.c
+++ b/drivers/net/wireless/ath/wil6210/sysfs.c
@@ -204,9 +204,74 @@ out:
static DEVICE_ATTR(thermal_throttling, 0644,
wil_tt_sysfs_show, wil_tt_sysfs_store);
+static ssize_t
+wil_fst_link_loss_sysfs_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++)
+ if (wil->sta[i].status == wil_sta_connected)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "[%d] %pM %s\n", i, wil->sta[i].addr,
+ wil->sta[i].fst_link_loss ?
+ "On" : "Off");
+
+ return len;
+}
+
+static ssize_t
+wil_fst_link_loss_sysfs_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ u8 addr[ETH_ALEN];
+ char *token, *dupbuf, *tmp;
+ int rc = -EINVAL;
+ bool fst_link_loss;
+
+ tmp = kmemdup(buf, count + 1, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ tmp[count] = '\0';
+ dupbuf = tmp;
+
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+
+ /* mac address */
+ if (sscanf(token, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &addr[0], &addr[1], &addr[2],
+ &addr[3], &addr[4], &addr[5]) != 6)
+ goto out;
+
+ /* On/Off */
+ if (strtobool(dupbuf, &fst_link_loss))
+ goto out;
+
+ wil_dbg_misc(wil, "set [%pM] with %d\n", addr, fst_link_loss);
+
+ rc = wmi_link_maintain_cfg_write(wil, addr, fst_link_loss);
+ if (!rc)
+ rc = count;
+
+out:
+ kfree(tmp);
+ return rc;
+}
+
+static DEVICE_ATTR(fst_link_loss, 0644,
+ wil_fst_link_loss_sysfs_show,
+ wil_fst_link_loss_sysfs_store);
+
static struct attribute *wil6210_sysfs_entries[] = {
&dev_attr_ftm_txrx_offset.attr,
&dev_attr_thermal_throttling.attr,
+ &dev_attr_fst_link_loss.attr,
NULL
};
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index cf9ed143fdf4..0529d10a8268 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -37,8 +37,13 @@ extern bool debug_fw;
extern bool disable_ap_sme;
#define WIL_NAME "wil6210"
-#define WIL_FW_NAME_DEFAULT "wil6210.fw" /* code Sparrow B0 */
-#define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw" /* code Sparrow D0 */
+
+#define WIL_FW_NAME_DEFAULT "wil6210.fw"
+#define WIL_FW_NAME_FTM_DEFAULT "wil6210_ftm.fw"
+
+#define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw"
+#define WIL_FW_NAME_FTM_SPARROW_PLUS "wil6210_sparrow_plus_ftm.fw"
+
#define WIL_BOARD_FILE_NAME "wil6210.brd" /* board & radio parameters */
#define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */
@@ -534,6 +539,7 @@ struct wil_sta_info {
struct wil_tid_crypto_rx tid_crypto_rx[WIL_STA_TID_NUM];
struct wil_tid_crypto_rx group_crypto_rx;
u8 aid; /* 1-254; 0 if unknown/not reported */
+ bool fst_link_loss;
};
enum {
@@ -990,5 +996,9 @@ void wil_ftm_evt_per_dest_res(struct wil6210_priv *wil,
void wil_aoa_evt_meas(struct wil6210_priv *wil,
struct wmi_aoa_meas_event *evt,
int len);
+/* link loss */
+int wmi_link_maintain_cfg_write(struct wil6210_priv *wil,
+ const u8 *addr,
+ bool fst_link_loss);
#endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 01a80a7b981e..41afbdc34c18 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -518,16 +518,16 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
assoc_resp_ielen = 0;
}
- mutex_lock(&wil->mutex);
if (test_bit(wil_status_resetting, wil->status) ||
!test_bit(wil_status_fwready, wil->status)) {
wil_err(wil, "status_resetting, cancel connect event, CID %d\n",
evt->cid);
- mutex_unlock(&wil->mutex);
/* no need for cleanup, wil_reset will do that */
return;
}
+ mutex_lock(&wil->mutex);
+
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
if (!test_bit(wil_status_fwconnecting, wil->status)) {
@@ -631,6 +631,13 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
wil->sinfo_gen++;
+ if (test_bit(wil_status_resetting, wil->status) ||
+ !test_bit(wil_status_fwready, wil->status)) {
+ wil_err(wil, "status_resetting, cancel disconnect event\n");
+ /* no need for cleanup, wil_reset will do that */
+ return;
+ }
+
mutex_lock(&wil->mutex);
wil6210_disconnect(wil, evt->bssid, reason_code, true);
mutex_unlock(&wil->mutex);
@@ -1846,6 +1853,61 @@ void wmi_event_flush(struct wil6210_priv *wil)
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
}
+int wmi_link_maintain_cfg_write(struct wil6210_priv *wil,
+ const u8 *addr,
+ bool fst_link_loss)
+{
+ int rc;
+ int cid = wil_find_cid(wil, addr);
+ u32 cfg_type;
+ struct wmi_link_maintain_cfg_write_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_link_maintain_cfg_write_done_event evt;
+ } __packed reply;
+
+ if (cid < 0)
+ return cid;
+
+ switch (wil->wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ cfg_type = fst_link_loss ?
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_STA :
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_STA;
+ break;
+ case NL80211_IFTYPE_AP:
+ cfg_type = fst_link_loss ?
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_AP :
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_AP;
+ break;
+ default:
+ wil_err(wil, "Unsupported for iftype %d", wil->wdev->iftype);
+ return -EINVAL;
+ }
+
+ wil_dbg_misc(wil, "Setting cid:%d with cfg_type:%d\n", cid, cfg_type);
+
+ cmd.cfg_type = cpu_to_le32(cfg_type);
+ cmd.cid = cpu_to_le32(cid);
+
+ reply.evt.status = cpu_to_le32(WMI_FW_STATUS_FAILURE);
+
+ rc = wmi_call(wil, WMI_LINK_MAINTAIN_CFG_WRITE_CMDID, &cmd, sizeof(cmd),
+ WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID, &reply,
+ sizeof(reply), 250);
+ if (rc) {
+ wil_err(wil, "Failed to %s FST link loss",
+ fst_link_loss ? "enable" : "disable");
+ } else if (reply.evt.status == WMI_FW_STATUS_SUCCESS) {
+ wil->sta[cid].fst_link_loss = fst_link_loss;
+ } else {
+ wil_err(wil, "WMI_LINK_MAINTAIN_CFG_WRITE_CMDID returned status %d",
+ reply.evt.status);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id,
void *d, int len)
{
diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
index e93416ebd343..09c37c2383c6 100644
--- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
+++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
@@ -181,7 +181,6 @@ void *wcnss_prealloc_get(unsigned int size)
pr_err("wcnss: %s: prealloc not available for size: %d\n",
__func__, size);
- WARN_ON(1);
return NULL;
}
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 3f9eeabc5464..505a9e016777 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -192,6 +192,8 @@ static DEFINE_SPINLOCK(reg_spinlock);
#define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3)
#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x"
+#define SHOW_MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x\n"
+#define WCNSS_USER_MAC_ADDR_LENGTH 18
/* message types */
#define WCNSS_CTRL_MSG_START 0x01000000
@@ -396,7 +398,6 @@ static struct {
int user_cal_available;
u32 user_cal_rcvd;
int user_cal_exp_size;
- int device_opened;
int iris_xo_mode_set;
int fw_vbatt_state;
char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE];
@@ -427,23 +428,28 @@ static struct {
static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- char macAddr[WLAN_MAC_ADDR_SIZE];
+ int index;
+ int macAddr[WLAN_MAC_ADDR_SIZE];
if (!penv)
return -ENODEV;
- pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf);
+ if (strlen(buf) != WCNSS_USER_MAC_ADDR_LENGTH) {
+ dev_err(dev, "%s: Invalid MAC addr length\n", __func__);
+ return -EINVAL;
+ }
if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR,
- (int *)&macAddr[0], (int *)&macAddr[1],
- (int *)&macAddr[2], (int *)&macAddr[3],
- (int *)&macAddr[4], (int *)&macAddr[5])) {
-
+ &macAddr[0], &macAddr[1], &macAddr[2],
+ &macAddr[3], &macAddr[4], &macAddr[5])) {
pr_err("%s: Failed to Copy MAC\n", __func__);
return -EINVAL;
}
- memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr));
+ for (index = 0; index < WLAN_MAC_ADDR_SIZE; index++) {
+ memcpy(&penv->wlan_nv_macAddr[index],
+ (char *)&macAddr[index], sizeof(char));
+ }
pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
@@ -459,7 +465,7 @@ static ssize_t wcnss_wlan_macaddr_show(struct device *dev,
if (!penv)
return -ENODEV;
- return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR,
+ return scnprintf(buf, PAGE_SIZE, SHOW_MAC_ADDRESS_STR,
penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
@@ -2038,21 +2044,23 @@ void extract_cal_data(int len)
return;
}
+ mutex_lock(&penv->dev_lock);
rc = smd_read(penv->smd_ch, (unsigned char *)&calhdr,
sizeof(struct cal_data_params));
if (rc < sizeof(struct cal_data_params)) {
pr_err("wcnss: incomplete cal header read from smd\n");
+ mutex_unlock(&penv->dev_lock);
return;
}
if (penv->fw_cal_exp_frag != calhdr.frag_number) {
pr_err("wcnss: Invalid frgament");
- goto exit;
+ goto unlock_exit;
}
if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) {
pr_err("wcnss: Invalid fragment size");
- goto exit;
+ goto unlock_exit;
}
if (penv->fw_cal_available) {
@@ -2061,8 +2069,9 @@ void extract_cal_data(int len)
penv->fw_cal_exp_frag++;
if (calhdr.msg_flags & LAST_FRAGMENT) {
penv->fw_cal_exp_frag = 0;
- goto exit;
+ goto unlock_exit;
}
+ mutex_unlock(&penv->dev_lock);
return;
}
@@ -2070,7 +2079,7 @@ void extract_cal_data(int len)
if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) {
pr_err("wcnss: Invalid cal data size %d",
calhdr.total_size);
- goto exit;
+ goto unlock_exit;
}
kfree(penv->fw_cal_data);
penv->fw_cal_rcvd = 0;
@@ -2078,11 +2087,10 @@ void extract_cal_data(int len)
GFP_KERNEL);
if (penv->fw_cal_data == NULL) {
smd_read(penv->smd_ch, NULL, calhdr.frag_size);
- goto exit;
+ goto unlock_exit;
}
}
- mutex_lock(&penv->dev_lock);
if (penv->fw_cal_rcvd + calhdr.frag_size >
MAX_CALIBRATED_DATA_SIZE) {
pr_err("calibrated data size is more than expected %d",
@@ -2117,13 +2125,10 @@ void extract_cal_data(int len)
unlock_exit:
mutex_unlock(&penv->dev_lock);
-
-exit:
wcnss_send_cal_rsp(fw_status);
return;
}
-
static void wcnssctrl_rx_handler(struct work_struct *worker)
{
int len = 0;
@@ -3258,14 +3263,6 @@ static int wcnss_node_open(struct inode *inode, struct file *file)
return -EFAULT;
}
- mutex_lock(&penv->dev_lock);
- penv->user_cal_rcvd = 0;
- penv->user_cal_read = 0;
- penv->user_cal_available = false;
- penv->user_cal_data = NULL;
- penv->device_opened = 1;
- mutex_unlock(&penv->dev_lock);
-
return rc;
}
@@ -3274,7 +3271,7 @@ static ssize_t wcnss_wlan_read(struct file *fp, char __user
{
int rc = 0;
- if (!penv || !penv->device_opened)
+ if (!penv)
return -EFAULT;
rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
@@ -3311,55 +3308,66 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user
*user_buffer, size_t count, loff_t *position)
{
int rc = 0;
- u32 size = 0;
+ char *cal_data = NULL;
- if (!penv || !penv->device_opened || penv->user_cal_available)
+ if (!penv || penv->user_cal_available)
return -EFAULT;
- if (penv->user_cal_rcvd == 0 && count >= 4
- && !penv->user_cal_data) {
- rc = copy_from_user((void *)&size, user_buffer, 4);
- if (!size || size > MAX_CALIBRATED_DATA_SIZE) {
- pr_err(DEVICE " invalid size to write %d\n", size);
+ if (!penv->user_cal_rcvd && count >= 4 && !penv->user_cal_exp_size) {
+ mutex_lock(&penv->dev_lock);
+ rc = copy_from_user((void *)&penv->user_cal_exp_size,
+ user_buffer, 4);
+ if (!penv->user_cal_exp_size ||
+ penv->user_cal_exp_size > MAX_CALIBRATED_DATA_SIZE) {
+ pr_err(DEVICE " invalid size to write %d\n",
+ penv->user_cal_exp_size);
+ penv->user_cal_exp_size = 0;
+ mutex_unlock(&penv->dev_lock);
return -EFAULT;
}
-
- rc += count;
- count -= 4;
- penv->user_cal_exp_size = size;
- penv->user_cal_data = kmalloc(size, GFP_KERNEL);
- if (penv->user_cal_data == NULL) {
- pr_err(DEVICE " no memory to write\n");
- return -ENOMEM;
- }
- if (0 == count)
- goto exit;
-
- } else if (penv->user_cal_rcvd == 0 && count < 4)
+ mutex_unlock(&penv->dev_lock);
+ return count;
+ } else if (!penv->user_cal_rcvd && count < 4) {
return -EFAULT;
+ }
+ mutex_lock(&penv->dev_lock);
if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
(penv->user_cal_exp_size < count + penv->user_cal_rcvd)) {
pr_err(DEVICE " invalid size to write %zu\n", count +
penv->user_cal_rcvd);
- rc = -ENOMEM;
- goto exit;
+ mutex_unlock(&penv->dev_lock);
+ return -ENOMEM;
}
- rc = copy_from_user((void *)penv->user_cal_data +
- penv->user_cal_rcvd, user_buffer, count);
- if (0 == rc) {
+
+ cal_data = kmalloc(count, GFP_KERNEL);
+ if (!cal_data) {
+ mutex_unlock(&penv->dev_lock);
+ return -ENOMEM;
+ }
+
+ rc = copy_from_user(cal_data, user_buffer, count);
+ if (!rc) {
+ memcpy(penv->user_cal_data + penv->user_cal_rcvd,
+ cal_data, count);
penv->user_cal_rcvd += count;
rc += count;
}
+
+ kfree(cal_data);
if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
penv->user_cal_available = true;
pr_info_ratelimited("wcnss: user cal written");
}
+ mutex_unlock(&penv->dev_lock);
-exit:
return rc;
}
+static int wcnss_node_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
void *ss_handle)
@@ -3418,6 +3426,7 @@ static const struct file_operations wcnss_node_fops = {
.open = wcnss_node_open,
.read = wcnss_wlan_read,
.write = wcnss_wlan_write,
+ .release = wcnss_node_release,
};
static struct miscdevice wcnss_misc = {
@@ -3445,6 +3454,13 @@ wcnss_wlan_probe(struct platform_device *pdev)
}
penv->pdev = pdev;
+ penv->user_cal_data =
+ devm_kzalloc(&pdev->dev, MAX_CALIBRATED_DATA_SIZE, GFP_KERNEL);
+ if (!penv->user_cal_data) {
+ dev_err(&pdev->dev, "Failed to alloc memory for cal data.\n");
+ return -ENOMEM;
+ }
+
/* register sysfs entries */
ret = wcnss_create_sysfs(&pdev->dev);
if (ret) {
@@ -3465,6 +3481,11 @@ wcnss_wlan_probe(struct platform_device *pdev)
mutex_init(&penv->pm_qos_mutex);
init_waitqueue_head(&penv->read_wait);
+ penv->user_cal_rcvd = 0;
+ penv->user_cal_read = 0;
+ penv->user_cal_exp_size = 0;
+ penv->user_cal_available = false;
+
/* Since we were built into the kernel we'll be called as part
* of kernel initialization. We don't know if userspace
* applications are available to service PIL at this time
diff --git a/drivers/parport/share.c b/drivers/parport/share.c
index 5ce5ef211bdb..754f21fd9768 100644
--- a/drivers/parport/share.c
+++ b/drivers/parport/share.c
@@ -936,8 +936,10 @@ parport_register_dev_model(struct parport *port, const char *name,
* pardevice fields. -arca
*/
port->ops->init_state(par_dev, par_dev->state);
- port->proc_device = par_dev;
- parport_device_proc_register(par_dev);
+ if (!test_and_set_bit(PARPORT_DEVPROC_REGISTERED, &port->devflags)) {
+ port->proc_device = par_dev;
+ parport_device_proc_register(par_dev);
+ }
return par_dev;
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 9dc678ce4a48..f364882943e1 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -5811,9 +5811,10 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev,
struct msi_msg *msg)
{
struct iommu_domain *domain = iommu_get_domain_for_dev(&pdev->dev);
- int ret, bypass_en = 0;
+ struct iommu_domain_geometry geometry;
+ int ret, fastmap_en = 0, bypass_en = 0;
dma_addr_t iova;
- phys_addr_t pcie_base_addr, gicm_db_offset;
+ phys_addr_t gicm_db_offset;
msg->address_hi = 0;
msg->address_lo = dev->msi_gicm_addr;
@@ -5835,16 +5836,25 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev,
if (bypass_en)
return 0;
- gicm_db_offset = dev->msi_gicm_addr -
- rounddown(dev->msi_gicm_addr, PAGE_SIZE);
- /*
- * Use PCIe DBI address as the IOVA since client cannot
- * use this address for their IOMMU mapping. This will
- * prevent any conflicts between PCIe host and
- * client's mapping.
- */
- pcie_base_addr = dev->res[MSM_PCIE_RES_DM_CORE].resource->start;
- iova = rounddown(pcie_base_addr, PAGE_SIZE);
+ iommu_domain_get_attr(domain, DOMAIN_ATTR_FAST, &fastmap_en);
+ if (fastmap_en) {
+ iommu_domain_get_attr(domain, DOMAIN_ATTR_GEOMETRY, &geometry);
+ iova = geometry.aperture_start;
+ PCIE_DBG(dev,
+ "PCIe: RC%d: Use client's IOVA 0x%llx to map QGIC MSI address\n",
+ dev->rc_idx, iova);
+ } else {
+ phys_addr_t pcie_base_addr;
+
+ /*
+ * Use PCIe DBI address as the IOVA since client cannot
+ * use this address for their IOMMU mapping. This will
+ * prevent any conflicts between PCIe host and
+ * client's mapping.
+ */
+ pcie_base_addr = dev->res[MSM_PCIE_RES_DM_CORE].resource->start;
+ iova = rounddown(pcie_base_addr, PAGE_SIZE);
+ }
ret = iommu_map(domain, iova, rounddown(dev->msi_gicm_addr, PAGE_SIZE),
PAGE_SIZE, IOMMU_READ | IOMMU_WRITE);
@@ -5855,6 +5865,8 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev,
return -ENOMEM;
}
+ gicm_db_offset = dev->msi_gicm_addr -
+ rounddown(dev->msi_gicm_addr, PAGE_SIZE);
msg->address_lo = iova + gicm_db_offset;
return 0;
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 31f31d460fc9..357527712539 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -303,13 +303,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
return rc;
}
- pci_iov_set_numvfs(dev, nr_virtfn);
- iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
- pci_cfg_access_lock(dev);
- pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
- msleep(100);
- pci_cfg_access_unlock(dev);
-
iov->initial_VFs = initial;
if (nr_virtfn < initial)
initial = nr_virtfn;
@@ -320,6 +313,13 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
goto err_pcibios;
}
+ pci_iov_set_numvfs(dev, nr_virtfn);
+ iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
+ pci_cfg_access_lock(dev);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
+ msleep(100);
+ pci_cfg_access_unlock(dev);
+
for (i = 0; i < initial; i++) {
rc = virtfn_add(dev, i, 0);
if (rc)
@@ -555,21 +555,61 @@ void pci_iov_release(struct pci_dev *dev)
}
/**
- * pci_iov_resource_bar - get position of the SR-IOV BAR
+ * pci_iov_update_resource - update a VF BAR
* @dev: the PCI device
* @resno: the resource number
*
- * Returns position of the BAR encapsulated in the SR-IOV capability.
+ * Update a VF BAR in the SR-IOV capability of a PF.
*/
-int pci_iov_resource_bar(struct pci_dev *dev, int resno)
+void pci_iov_update_resource(struct pci_dev *dev, int resno)
{
- if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
- return 0;
+ struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL;
+ struct resource *res = dev->resource + resno;
+ int vf_bar = resno - PCI_IOV_RESOURCES;
+ struct pci_bus_region region;
+ u16 cmd;
+ u32 new;
+ int reg;
+
+ /*
+ * The generic pci_restore_bars() path calls this for all devices,
+ * including VFs and non-SR-IOV devices. If this is not a PF, we
+ * have nothing to do.
+ */
+ if (!iov)
+ return;
+
+ pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &cmd);
+ if ((cmd & PCI_SRIOV_CTRL_VFE) && (cmd & PCI_SRIOV_CTRL_MSE)) {
+ dev_WARN(&dev->dev, "can't update enabled VF BAR%d %pR\n",
+ vf_bar, res);
+ return;
+ }
+
+ /*
+ * Ignore unimplemented BARs, unused resource slots for 64-bit
+ * BARs, and non-movable resources, e.g., those described via
+ * Enhanced Allocation.
+ */
+ if (!res->flags)
+ return;
+
+ if (res->flags & IORESOURCE_UNSET)
+ return;
+
+ if (res->flags & IORESOURCE_PCI_FIXED)
+ return;
- BUG_ON(!dev->is_physfn);
+ pcibios_resource_to_bus(dev->bus, &region, res);
+ new = region.start;
+ new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
- return dev->sriov->pos + PCI_SRIOV_BAR +
- 4 * (resno - PCI_IOV_RESOURCES);
+ reg = iov->pos + PCI_SRIOV_BAR + 4 * vf_bar;
+ pci_write_config_dword(dev, reg, new);
+ if (res->flags & IORESOURCE_MEM_64) {
+ new = region.start >> 16 >> 16;
+ pci_write_config_dword(dev, reg + 4, new);
+ }
}
resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev,
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e311a9bf2c90..0e53488f8ec1 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -519,10 +519,6 @@ static void pci_restore_bars(struct pci_dev *dev)
{
int i;
- /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */
- if (dev->is_virtfn)
- return;
-
for (i = 0; i < PCI_BRIDGE_RESOURCES; i++)
pci_update_resource(dev, i);
}
@@ -4472,36 +4468,6 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags)
}
EXPORT_SYMBOL(pci_select_bars);
-/**
- * pci_resource_bar - get position of the BAR associated with a resource
- * @dev: the PCI device
- * @resno: the resource number
- * @type: the BAR type to be filled in
- *
- * Returns BAR position in config space, or 0 if the BAR is invalid.
- */
-int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type)
-{
- int reg;
-
- if (resno < PCI_ROM_RESOURCE) {
- *type = pci_bar_unknown;
- return PCI_BASE_ADDRESS_0 + 4 * resno;
- } else if (resno == PCI_ROM_RESOURCE) {
- *type = pci_bar_mem32;
- return dev->rom_base_reg;
- } else if (resno < PCI_BRIDGE_RESOURCES) {
- /* device specific resource */
- *type = pci_bar_unknown;
- reg = pci_iov_resource_bar(dev, resno);
- if (reg)
- return reg;
- }
-
- dev_err(&dev->dev, "BAR %d: invalid resource\n", resno);
- return 0;
-}
-
/* Some architectures require additional programming to enable VGA */
static arch_set_vga_state_t arch_set_vga_state;
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index d390fc1475ec..c43e448873ca 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -232,7 +232,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
int pci_setup_device(struct pci_dev *dev);
int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
struct resource *res, unsigned int reg);
-int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type);
void pci_configure_ari(struct pci_dev *dev);
void __pci_bus_size_bridges(struct pci_bus *bus,
struct list_head *realloc_head);
@@ -276,7 +275,7 @@ static inline void pci_restore_ats_state(struct pci_dev *dev)
#ifdef CONFIG_PCI_IOV
int pci_iov_init(struct pci_dev *dev);
void pci_iov_release(struct pci_dev *dev);
-int pci_iov_resource_bar(struct pci_dev *dev, int resno);
+void pci_iov_update_resource(struct pci_dev *dev, int resno);
resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
void pci_restore_iov_state(struct pci_dev *dev);
int pci_iov_bus_range(struct pci_bus *bus);
@@ -290,10 +289,6 @@ static inline void pci_iov_release(struct pci_dev *dev)
{
}
-static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno)
-{
- return 0;
-}
static inline void pci_restore_iov_state(struct pci_dev *dev)
{
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 71d9a6d1bd56..b83df942794f 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -226,7 +226,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK;
}
} else {
- res->flags |= (l & IORESOURCE_ROM_ENABLE);
+ if (l & PCI_ROM_ADDRESS_ENABLE)
+ res->flags |= IORESOURCE_ROM_ENABLE;
l64 = l & PCI_ROM_ADDRESS_MASK;
sz64 = sz & PCI_ROM_ADDRESS_MASK;
mask64 = (u32)PCI_ROM_ADDRESS_MASK;
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index eb0ad530dc43..3eea7fc5e1a2 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -31,6 +31,11 @@ int pci_enable_rom(struct pci_dev *pdev)
if (!res->flags)
return -1;
+ /*
+ * Ideally pci_update_resource() would update the ROM BAR address,
+ * and we would only set the enable bit here. But apparently some
+ * devices have buggy ROM BARs that read as zero when disabled.
+ */
pcibios_resource_to_bus(pdev->bus, &region, res);
pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
rom_addr &= ~PCI_ROM_ADDRESS_MASK;
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 604011e047d6..25062966cbfa 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -25,21 +25,18 @@
#include <linux/slab.h>
#include "pci.h"
-
-void pci_update_resource(struct pci_dev *dev, int resno)
+static void pci_std_update_resource(struct pci_dev *dev, int resno)
{
struct pci_bus_region region;
bool disable;
u16 cmd;
u32 new, check, mask;
int reg;
- enum pci_bar_type type;
struct resource *res = dev->resource + resno;
- if (dev->is_virtfn) {
- dev_warn(&dev->dev, "can't update VF BAR%d\n", resno);
+ /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */
+ if (dev->is_virtfn)
return;
- }
/*
* Ignore resources for unimplemented BARs and unused resource slots
@@ -60,21 +57,34 @@ void pci_update_resource(struct pci_dev *dev, int resno)
return;
pcibios_resource_to_bus(dev->bus, &region, res);
+ new = region.start;
- new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
- if (res->flags & IORESOURCE_IO)
+ if (res->flags & IORESOURCE_IO) {
mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
- else
+ new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK;
+ } else if (resno == PCI_ROM_RESOURCE) {
+ mask = (u32)PCI_ROM_ADDRESS_MASK;
+ } else {
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
+ new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
+ }
- reg = pci_resource_bar(dev, resno, &type);
- if (!reg)
- return;
- if (type != pci_bar_unknown) {
+ if (resno < PCI_ROM_RESOURCE) {
+ reg = PCI_BASE_ADDRESS_0 + 4 * resno;
+ } else if (resno == PCI_ROM_RESOURCE) {
+
+ /*
+ * Apparently some Matrox devices have ROM BARs that read
+ * as zero when disabled, so don't update ROM BARs unless
+ * they're enabled. See https://lkml.org/lkml/2005/8/30/138.
+ */
if (!(res->flags & IORESOURCE_ROM_ENABLE))
return;
+
+ reg = dev->rom_base_reg;
new |= PCI_ROM_ADDRESS_ENABLE;
- }
+ } else
+ return;
/*
* We can't update a 64-bit BAR atomically, so when possible,
@@ -110,6 +120,16 @@ void pci_update_resource(struct pci_dev *dev, int resno)
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
+void pci_update_resource(struct pci_dev *dev, int resno)
+{
+ if (resno <= PCI_ROM_RESOURCE)
+ pci_std_update_resource(dev, resno);
+#ifdef CONFIG_PCI_IOV
+ else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
+ pci_iov_update_resource(dev, resno);
+#endif
+}
+
int pci_claim_resource(struct pci_dev *dev, int resource)
{
struct resource *res = &dev->resource[resource];
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 4c75b4d392c6..39400dda27c2 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -367,6 +367,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu)
return err;
}
+ armpmu->pmu_state = ARM_PMU_STATE_RUNNING;
+
return 0;
}
@@ -601,10 +603,12 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
struct platform_device *pmu_device = cpu_pmu->plat_device;
struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events;
+ cpu_pmu->pmu_state = ARM_PMU_STATE_GOING_DOWN;
+
irqs = min(pmu_device->num_resources, num_possible_cpus());
irq = platform_get_irq(pmu_device, 0);
- if (irq >= 0 && irq_is_percpu(irq)) {
+ if (irq > 0 && irq_is_percpu(irq)) {
on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1);
free_percpu_irq(irq, &hw_events->percpu_pmu);
} else {
@@ -617,10 +621,11 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
if (!cpumask_test_and_clear_cpu(cpu, &cpu_pmu->active_irqs))
continue;
irq = platform_get_irq(pmu_device, i);
- if (irq >= 0)
+ if (irq > 0)
free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu));
}
}
+ cpu_pmu->pmu_state = ARM_PMU_STATE_OFF;
}
static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
@@ -639,7 +644,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
}
irq = platform_get_irq(pmu_device, 0);
- if (irq >= 0 && irq_is_percpu(irq)) {
+ if (irq > 0 && irq_is_percpu(irq)) {
err = request_percpu_irq(irq, handler, "arm-pmu",
&hw_events->percpu_pmu);
if (err) {
@@ -648,6 +653,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
return err;
}
on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1);
+ cpu_pmu->percpu_irq = irq;
} else {
for (i = 0; i < irqs; ++i) {
int cpu = i;
@@ -754,13 +760,6 @@ static void cpu_pm_pmu_common(void *info)
return;
}
- /*
- * Always reset the PMU registers on power-up even if
- * there are no events running.
- */
- if (cmd == CPU_PM_EXIT && armpmu->reset)
- armpmu->reset(armpmu);
-
if (!enabled) {
data->ret = NOTIFY_OK;
return;
@@ -795,6 +794,13 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
.cpu = smp_processor_id(),
};
+ /*
+ * Always reset the PMU registers on power-up even if
+ * there are no events running.
+ */
+ if (cmd == CPU_PM_EXIT && data.armpmu->reset)
+ data.armpmu->reset(data.armpmu);
+
cpu_pm_pmu_common(&data);
return data.ret;
}
@@ -824,6 +830,7 @@ static inline void cpu_pm_pmu_common(void *info) { }
static int cpu_pmu_notify(struct notifier_block *b, unsigned long action,
void *hcpu)
{
+ int irq = -1;
unsigned long masked_action = (action & ~CPU_TASKS_FROZEN);
struct cpu_pm_pmu_args data = {
.armpmu = container_of(b, struct arm_pmu, hotplug_nb),
@@ -835,37 +842,37 @@ static int cpu_pmu_notify(struct notifier_block *b, unsigned long action,
switch (masked_action) {
case CPU_STARTING:
- data.cmd = CPU_PM_EXIT;
- break;
- case CPU_DYING:
- data.cmd = CPU_PM_ENTER;
- break;
case CPU_DOWN_FAILED:
- data.cmd = CPU_PM_ENTER_FAILED;
- break;
- case CPU_ONLINE:
- if (data.armpmu->plat_device) {
- struct platform_device *pmu_device =
- data.armpmu->plat_device;
- int irq = platform_get_irq(pmu_device, 0);
-
- if (irq >= 0 && irq_is_percpu(irq)) {
- smp_call_function_single(data.cpu,
- cpu_pmu_enable_percpu_irq, &irq, 1);
- }
+ /*
+ * Always reset the PMU registers on power-up even if
+ * there are no events running.
+ */
+ if (data.armpmu->reset)
+ data.armpmu->reset(data.armpmu);
+ if (data.armpmu->pmu_state == ARM_PMU_STATE_RUNNING) {
+ if (data.armpmu->plat_device)
+ irq = data.armpmu->percpu_irq;
+ /* Arm the PMU IRQ before appearing. */
+ if (irq > 0 && irq_is_percpu(irq))
+ cpu_pmu_enable_percpu_irq(&irq);
+ data.cmd = CPU_PM_EXIT;
+ cpu_pm_pmu_common(&data);
}
- return NOTIFY_DONE;
+ return NOTIFY_OK;
+ case CPU_DYING:
+ if (data.armpmu->pmu_state != ARM_PMU_STATE_OFF) {
+ data.cmd = CPU_PM_ENTER;
+ cpu_pm_pmu_common(&data);
+ /* Disarm the PMU IRQ before disappearing. */
+ if (data.armpmu->plat_device)
+ irq = data.armpmu->percpu_irq;
+ if (irq > 0 && irq_is_percpu(irq))
+ cpu_pmu_disable_percpu_irq(&irq);
+ }
+ return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
-
- if (smp_processor_id() == data.cpu)
- cpu_pm_pmu_common(&data);
- else
- smp_call_function_single(data.cpu,
- cpu_pm_pmu_common, &data, 1);
-
- return data.ret;
}
static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
@@ -966,7 +973,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
/* Check the IRQ type and prohibit a mix of PPIs and SPIs */
irq = platform_get_irq(pdev, i);
- if (irq >= 0) {
+ if (irq > 0) {
bool spi = !irq_is_percpu(irq);
if (i > 0 && spi != using_spi) {
@@ -1085,6 +1092,9 @@ int arm_pmu_device_probe(struct platform_device *pdev,
if (ret)
goto out_destroy;
+ pmu->pmu_state = ARM_PMU_STATE_OFF;
+ pmu->percpu_irq = -1;
+
pr_info("enabled with %s PMU driver, %d counters available\n",
pmu->name, pmu->num_events);
diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c
index a009ae34c5ef..930f0f25c1ce 100644
--- a/drivers/pinctrl/intel/pinctrl-cherryview.c
+++ b/drivers/pinctrl/intel/pinctrl-cherryview.c
@@ -1466,12 +1466,11 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq)
offset += range->npins;
}
- /* Mask and clear all interrupts */
- chv_writel(0, pctrl->regs + CHV_INTMASK);
+ /* Clear all interrupts */
chv_writel(0xffff, pctrl->regs + CHV_INTSTAT);
ret = gpiochip_irqchip_add(chip, &chv_gpio_irqchip, 0,
- handle_simple_irq, IRQ_TYPE_NONE);
+ handle_bad_irq, IRQ_TYPE_NONE);
if (ret) {
dev_err(pctrl->dev, "failed to add IRQ chip\n");
goto fail;
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index c1448955d3ed..c5a351e7bb4e 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -602,10 +602,6 @@ static void msm_gpio_irq_unmask(struct irq_data *d)
spin_lock_irqsave(&pctrl->lock, flags);
- val = readl(pctrl->regs + g->intr_status_reg);
- val &= ~BIT(g->intr_status_bit);
- writel(val, pctrl->regs + g->intr_status_reg);
-
val = readl(pctrl->regs + g->intr_cfg_reg);
val |= BIT(g->intr_enable_bit);
writel(val, pctrl->regs + g->intr_cfg_reg);
diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c
index 717c891788f2..eaf50ca1cea5 100644
--- a/drivers/platform/msm/gsi/gsi_dbg.c
+++ b/drivers/platform/msm/gsi/gsi_dbg.c
@@ -29,6 +29,7 @@
static struct dentry *dent;
static char dbg_buff[4096];
+static void *gsi_ipc_logbuf_low;
static void gsi_wq_print_dp_stats(struct work_struct *work);
static DECLARE_DELAYED_WORK(gsi_print_dp_stats_work, gsi_wq_print_dp_stats);
@@ -764,22 +765,20 @@ static ssize_t gsi_enable_ipc_low(struct file *file,
if (kstrtos8(dbg_buff, 0, &option))
return -EFAULT;
+ mutex_lock(&gsi_ctx->mlock);
if (option) {
- if (!gsi_ctx->ipc_logbuf_low) {
- gsi_ctx->ipc_logbuf_low =
+ if (!gsi_ipc_logbuf_low) {
+ gsi_ipc_logbuf_low =
ipc_log_context_create(GSI_IPC_LOG_PAGES,
"gsi_low", 0);
+ if (gsi_ipc_logbuf_low == NULL)
+ TERR("failed to get ipc_logbuf_low\n");
}
-
- if (gsi_ctx->ipc_logbuf_low == NULL) {
- TERR("failed to get ipc_logbuf_low\n");
- return -EFAULT;
- }
+ gsi_ctx->ipc_logbuf_low = gsi_ipc_logbuf_low;
} else {
- if (gsi_ctx->ipc_logbuf_low)
- ipc_log_context_destroy(gsi_ctx->ipc_logbuf_low);
gsi_ctx->ipc_logbuf_low = NULL;
}
+ mutex_unlock(&gsi_ctx->mlock);
return count;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 100bbd582a5e..f01743d04e84 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -1584,6 +1584,7 @@ static int ipa_init_smem_region(int memory_region_size,
struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL;
struct ipa_desc desc;
struct ipa_mem_buffer mem;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc;
if (memory_region_size == 0)
@@ -1603,7 +1604,7 @@ static int ipa_init_smem_region(int memory_region_size,
memset(mem.base, 0, mem.size);
cmd = kzalloc(sizeof(*cmd),
- GFP_KERNEL);
+ flag);
if (cmd == NULL) {
IPAERR("Failed to alloc immediate command object\n");
rc = -ENOMEM;
@@ -2166,6 +2167,7 @@ int _ipa_init_sram_v2(void)
struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL;
struct ipa_desc desc = {0};
struct ipa_mem_buffer mem;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc = 0;
phys_addr = ipa_ctx->ipa_wrapper_base +
@@ -2203,7 +2205,7 @@ int _ipa_init_sram_v2(void)
}
memset(mem.base, 0, mem.size);
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ cmd = kzalloc(sizeof(*cmd), flag);
if (cmd == NULL) {
IPAERR("Failed to alloc immediate command object\n");
rc = -ENOMEM;
@@ -2314,6 +2316,7 @@ int _ipa_init_hdr_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_hdr_init_local *cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc = 0;
mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size);
@@ -2325,7 +2328,7 @@ int _ipa_init_hdr_v2(void)
}
memset(mem.base, 0, mem.size);
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ cmd = kzalloc(sizeof(*cmd), flag);
if (cmd == NULL) {
IPAERR("Failed to alloc header init command object\n");
rc = -ENOMEM;
@@ -2360,6 +2363,7 @@ int _ipa_init_hdr_v2_5(void)
struct ipa_mem_buffer mem;
struct ipa_hdr_init_local *cmd = NULL;
struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size);
mem.base = dma_alloc_coherent(ipa_ctx->pdev, mem.size, &mem.phys_base,
@@ -2370,7 +2374,7 @@ int _ipa_init_hdr_v2_5(void)
}
memset(mem.base, 0, mem.size);
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ cmd = kzalloc(sizeof(*cmd), flag);
if (cmd == NULL) {
IPAERR("Failed to alloc header init command object\n");
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
@@ -2411,7 +2415,7 @@ int _ipa_init_hdr_v2_5(void)
memset(mem.base, 0, mem.size);
memset(&desc, 0, sizeof(desc));
- dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_KERNEL);
+ dma_cmd = kzalloc(sizeof(*dma_cmd), flag);
if (dma_cmd == NULL) {
IPAERR("Failed to alloc immediate command object\n");
dma_free_coherent(ipa_ctx->pdev,
@@ -2462,6 +2466,7 @@ int _ipa_init_rt4_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_ip_v4_routing_init *v4_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u32 *entry;
int i;
int rc = 0;
@@ -2486,7 +2491,7 @@ int _ipa_init_rt4_v2(void)
entry++;
}
- v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL);
+ v4_cmd = kzalloc(sizeof(*v4_cmd), flag);
if (v4_cmd == NULL) {
IPAERR("Failed to alloc v4 routing init command object\n");
rc = -ENOMEM;
@@ -2522,6 +2527,7 @@ int _ipa_init_rt6_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_ip_v6_routing_init *v6_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u32 *entry;
int i;
int rc = 0;
@@ -2546,7 +2552,7 @@ int _ipa_init_rt6_v2(void)
entry++;
}
- v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL);
+ v6_cmd = kzalloc(sizeof(*v6_cmd), flag);
if (v6_cmd == NULL) {
IPAERR("Failed to alloc v6 routing init command object\n");
rc = -ENOMEM;
@@ -2582,6 +2588,7 @@ int _ipa_init_flt4_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_ip_v4_filter_init *v4_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u32 *entry;
int i;
int rc = 0;
@@ -2604,7 +2611,7 @@ int _ipa_init_flt4_v2(void)
entry++;
}
- v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL);
+ v4_cmd = kzalloc(sizeof(*v4_cmd), flag);
if (v4_cmd == NULL) {
IPAERR("Failed to alloc v4 fliter init command object\n");
rc = -ENOMEM;
@@ -2640,6 +2647,7 @@ int _ipa_init_flt6_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_ip_v6_filter_init *v6_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u32 *entry;
int i;
int rc = 0;
@@ -2662,7 +2670,7 @@ int _ipa_init_flt6_v2(void)
entry++;
}
- v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL);
+ v6_cmd = kzalloc(sizeof(*v6_cmd), flag);
if (v6_cmd == NULL) {
IPAERR("Failed to alloc v6 fliter init command object\n");
rc = -ENOMEM;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index 8d2213f30ec2..e48c19c522a7 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -123,6 +123,7 @@ static struct dentry *dfile_ipa_poll_iteration;
static char dbg_buff[IPA_MAX_MSG_LEN];
static char *active_clients_buf;
static s8 ep_reg_idx;
+static void *ipa_ipc_low_buff;
int _ipa_read_gen_reg_v1_1(char *buff, int max_len)
{
@@ -1823,23 +1824,20 @@ static ssize_t ipa_enable_ipc_low(struct file *file,
if (kstrtos8(dbg_buff, 0, &option))
return -EFAULT;
+ mutex_lock(&ipa_ctx->lock);
if (option) {
- if (!ipa_ctx->logbuf_low) {
- ipa_ctx->logbuf_low =
+ if (!ipa_ipc_low_buff) {
+ ipa_ipc_low_buff =
ipc_log_context_create(IPA_IPC_LOG_PAGES,
"ipa_low", 0);
+ if (ipa_ipc_low_buff == NULL)
+ IPAERR("failed to get logbuf_low\n");
}
-
- if (ipa_ctx->logbuf_low == NULL) {
- IPAERR("failed to get logbuf_low\n");
- return -EFAULT;
- }
-
+ ipa_ctx->logbuf_low = ipa_ipc_low_buff;
} else {
- if (ipa_ctx->logbuf_low)
- ipc_log_context_destroy(ipa_ctx->logbuf_low);
- ipa_ctx->logbuf_low = NULL;
+ ipa_ctx->logbuf_low = NULL;
}
+ mutex_unlock(&ipa_ctx->lock);
return count;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 25364e8efa38..23c8a5059c3b 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -322,8 +322,8 @@ int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc,
dma_address = desc->dma_address;
tx_pkt->no_unmap_dma = true;
}
- if (!dma_address) {
- IPAERR("failed to DMA wrap\n");
+ if (dma_mapping_error(ipa_ctx->pdev, dma_address)) {
+ IPAERR("dma_map_single failed\n");
goto fail_dma_map;
}
@@ -445,7 +445,7 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
}
dma_addr = dma_map_single(ipa_ctx->pdev,
transfer.iovec, size, DMA_TO_DEVICE);
- if (!dma_addr) {
+ if (dma_mapping_error(ipa_ctx->pdev, dma_addr)) {
IPAERR("dma_map_single failed for sps xfr buff\n");
kfree(transfer.iovec);
return -EFAULT;
@@ -493,6 +493,15 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
tx_pkt->mem.base,
tx_pkt->mem.size,
DMA_TO_DEVICE);
+
+ if (dma_mapping_error(ipa_ctx->pdev,
+ tx_pkt->mem.phys_base)) {
+ IPAERR("dma_map_single ");
+ IPAERR("failed\n");
+ fail_dma_wrap = 1;
+ goto failure;
+ }
+
} else {
tx_pkt->mem.phys_base = desc[i].dma_address;
tx_pkt->no_unmap_dma = true;
@@ -1874,8 +1883,8 @@ begin:
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr,
sys->rx_buff_sz,
DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa_ctx->pdev,
+ rx_pkt->data.dma_addr)) {
pr_err_ratelimited("%s dma map fail %p for %p sys=%p\n",
__func__, (void *)rx_pkt->data.dma_addr,
ptr, sys);
@@ -2030,18 +2039,20 @@ static void ipa_alloc_wlan_rx_common_cache(u32 size)
ptr = skb_put(rx_pkt->data.skb, IPA_WLAN_RX_BUFF_SZ);
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr,
IPA_WLAN_RX_BUFF_SZ, DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa_ctx->pdev,
+ rx_pkt->data.dma_addr)) {
IPAERR("dma_map_single failure %p for %p\n",
(void *)rx_pkt->data.dma_addr, ptr);
goto fail_dma_mapping;
}
+ spin_lock_bh(&ipa_ctx->wc_memb.wlan_spinlock);
list_add_tail(&rx_pkt->link,
&ipa_ctx->wc_memb.wlan_comm_desc_list);
rx_len_cached = ++ipa_ctx->wc_memb.wlan_comm_total_cnt;
ipa_ctx->wc_memb.wlan_comm_free_cnt++;
+ spin_unlock_bh(&ipa_ctx->wc_memb.wlan_spinlock);
}
@@ -2102,8 +2113,8 @@ static void ipa_replenish_rx_cache(struct ipa_sys_context *sys)
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr,
sys->rx_buff_sz,
DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa_ctx->pdev,
+ rx_pkt->data.dma_addr)) {
IPAERR("dma_map_single failure %p for %p\n",
(void *)rx_pkt->data.dma_addr, ptr);
goto fail_dma_mapping;
@@ -2160,9 +2171,10 @@ static void ipa_replenish_rx_cache_recycle(struct ipa_sys_context *sys)
ptr = skb_put(rx_pkt->data.skb, sys->rx_buff_sz);
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev,
ptr, sys->rx_buff_sz, DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0)
+ if (dma_mapping_error(ipa_ctx->pdev, rx_pkt->data.dma_addr)) {
+ IPAERR("dma_map_single failure for rx_pkt\n");
goto fail_dma_mapping;
+ }
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index e23de3f26613..f43981f15c31 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -268,6 +268,7 @@ int __ipa_commit_hdr_v2(void)
struct ipa_mem_buffer mem;
struct ipa_hdr_init_system *cmd = NULL;
struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL;
+ gfp_t flag = GFP_ATOMIC | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc = -EFAULT;
if (ipa_generate_hdr_hw_tbl(&mem)) {
@@ -281,7 +282,7 @@ int __ipa_commit_hdr_v2(void)
IPA_MEM_PART(apps_hdr_size));
goto fail_send_cmd;
} else {
- dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_ATOMIC);
+ dma_cmd = kzalloc(sizeof(*dma_cmd), flag);
if (dma_cmd == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -303,7 +304,7 @@ int __ipa_commit_hdr_v2(void)
IPA_MEM_PART(apps_hdr_size_ddr));
goto fail_send_cmd;
} else {
- cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+ cmd = kzalloc(sizeof(*cmd), flag);
if (cmd == NULL) {
IPAERR("fail to alloc hdr init cmd\n");
rc = -ENOMEM;
@@ -359,6 +360,7 @@ int __ipa_commit_hdr_v2_5(void)
struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_hdr = NULL;
struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_ctx = NULL;
struct ipa_register_write *reg_write_cmd = NULL;
+ gfp_t flag = GFP_ATOMIC | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc = -EFAULT;
u32 proc_ctx_size;
u32 proc_ctx_ofst;
@@ -383,7 +385,7 @@ int __ipa_commit_hdr_v2_5(void)
IPA_MEM_PART(apps_hdr_size));
goto fail_send_cmd1;
} else {
- dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), GFP_ATOMIC);
+ dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), flag);
if (dma_cmd_hdr == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -406,7 +408,7 @@ int __ipa_commit_hdr_v2_5(void)
goto fail_send_cmd1;
} else {
hdr_init_cmd = kzalloc(sizeof(*hdr_init_cmd),
- GFP_ATOMIC);
+ flag);
if (hdr_init_cmd == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -431,7 +433,7 @@ int __ipa_commit_hdr_v2_5(void)
goto fail_send_cmd1;
} else {
dma_cmd_ctx = kzalloc(sizeof(*dma_cmd_ctx),
- GFP_ATOMIC);
+ flag);
if (dma_cmd_ctx == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -456,7 +458,7 @@ int __ipa_commit_hdr_v2_5(void)
goto fail_send_cmd1;
} else {
reg_write_cmd = kzalloc(sizeof(*reg_write_cmd),
- GFP_ATOMIC);
+ flag);
if (reg_write_cmd == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -722,6 +724,11 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
entry->hdr,
entry->hdr_len,
DMA_TO_DEVICE);
+ if (dma_mapping_error(ipa_ctx->pdev,
+ entry->phys_base)) {
+ IPAERR("dma_map_single failure for entry\n");
+ goto fail_dma_mapping;
+ }
}
} else {
entry->is_hdr_proc_ctx = false;
@@ -798,6 +805,8 @@ fail_add_proc_ctx:
list_del(&entry->link);
dma_unmap_single(ipa_ctx->pdev, entry->phys_base,
entry->hdr_len, DMA_TO_DEVICE);
+fail_dma_mapping:
+ entry->is_hdr_proc_ctx = false;
bad_hdr_len:
entry->cookie = 0;
kmem_cache_free(ipa_ctx->hdr_cache, entry);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index f5dea76764f8..4b6bc5b61bfc 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -698,6 +698,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
struct ipa_mem_buffer head;
struct ipa_hw_imm_cmd_dma_shared_mem *cmd1 = NULL;
struct ipa_hw_imm_cmd_dma_shared_mem *cmd2 = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u16 avail;
u32 num_modem_rt_index;
int rc = 0;
@@ -748,7 +749,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
}
cmd1 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
- GFP_KERNEL);
+ flag);
if (cmd1 == NULL) {
IPAERR("Failed to alloc immediate command object\n");
rc = -ENOMEM;
@@ -765,7 +766,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
if (lcl) {
cmd2 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
- GFP_KERNEL);
+ flag);
if (cmd2 == NULL) {
IPAERR("Failed to alloc immediate command object\n");
rc = -ENOMEM;
@@ -1353,6 +1354,10 @@ int ipa2_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup)
mutex_lock(&ipa_ctx->lock);
entry = __ipa_find_rt_tbl(lookup->ip, lookup->name);
if (entry && entry->cookie == IPA_COOKIE) {
+ if (entry->ref_cnt == U32_MAX) {
+ IPAERR("fail: ref count crossed limit\n");
+ goto ret;
+ }
entry->ref_cnt++;
lookup->hdl = entry->id;
@@ -1362,6 +1367,8 @@ int ipa2_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup)
result = 0;
}
+
+ret:
mutex_unlock(&ipa_ctx->lock);
return result;
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 246f6b68b839..774077494ea5 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1542,6 +1542,9 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
memcpy(mux_channel[rmnet_index].vchannel_name,
extend_ioctl_data.u.rmnet_mux_val.vchannel_name,
sizeof(mux_channel[rmnet_index].vchannel_name));
+ mux_channel[rmnet_index].vchannel_name[
+ IFNAMSIZ - 1] = '\0';
+
IPAWANDBG("cashe device[%s:%d] in IPA_wan[%d]\n",
mux_channel[rmnet_index].vchannel_name,
mux_channel[rmnet_index].mux_id,
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index ddff50834f03..5ee6e5d2d9e3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -2276,6 +2276,36 @@ static int ipa3_q6_set_ex_path_to_apps(void)
desc[num_descs].len = cmd_pyld->len;
num_descs++;
}
+
+ /* disable statuses for modem producers */
+ if (IPA_CLIENT_IS_Q6_PROD(client_idx)) {
+ ipa_assert_on(num_descs >= ipa3_ctx->ipa_num_pipes);
+
+ reg_write.skip_pipeline_clear = false;
+ reg_write.pipeline_clear_options =
+ IPAHAL_HPS_CLEAR;
+ reg_write.offset =
+ ipahal_get_reg_n_ofst(IPA_ENDP_STATUS_n,
+ ep_idx);
+ reg_write.value = 0;
+ reg_write.value_mask = ~0;
+ cmd_pyld = ipahal_construct_imm_cmd(
+ IPA_IMM_CMD_REGISTER_WRITE, &reg_write, false);
+ if (!cmd_pyld) {
+ IPAERR("fail construct register_write cmd\n");
+ ipa_assert();
+ return -EFAULT;
+ }
+
+ desc[num_descs].opcode = ipahal_imm_cmd_get_opcode(
+ IPA_IMM_CMD_REGISTER_WRITE);
+ desc[num_descs].type = IPA_IMM_CMD_DESC;
+ desc[num_descs].callback = ipa3_destroy_imm;
+ desc[num_descs].user1 = cmd_pyld;
+ desc[num_descs].pyld = cmd_pyld->data;
+ desc[num_descs].len = cmd_pyld->len;
+ num_descs++;
+ }
}
/* Will wait 500msecs for IPA tag process completion */
@@ -4036,6 +4066,7 @@ fail_register_device:
unregister_chrdev_region(ipa3_ctx->dev_num, 1);
if (ipa3_ctx->pipe_mem_pool)
gen_pool_destroy(ipa3_ctx->pipe_mem_pool);
+ ipa3_free_dma_task_for_gsi();
ipa3_destroy_flt_tbl_idrs();
idr_destroy(&ipa3_ctx->ipa_idr);
kmem_cache_destroy(ipa3_ctx->rx_pkt_wrapper_cache);
@@ -4551,6 +4582,13 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
goto fail_dma_pool;
}
+ /* allocate memory for DMA_TASK workaround */
+ result = ipa3_allocate_dma_task_for_gsi();
+ if (result) {
+ IPAERR("failed to allocate dma task\n");
+ goto fail_dma_task;
+ }
+
/* init the various list heads */
INIT_LIST_HEAD(&ipa3_ctx->hdr_tbl.head_hdr_entry_list);
for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
@@ -4723,6 +4761,8 @@ fail_cdev_add:
fail_device_create:
unregister_chrdev_region(ipa3_ctx->dev_num, 1);
fail_alloc_chrdev_region:
+ ipa3_free_dma_task_for_gsi();
+fail_dma_task:
if (ipa3_ctx->pipe_mem_pool)
gen_pool_destroy(ipa3_ctx->pipe_mem_pool);
ipa3_destroy_flt_tbl_idrs();
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 17e6973b807f..72eb3808c7a4 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -106,6 +106,7 @@ static char dbg_buff[IPA_MAX_MSG_LEN];
static char *active_clients_buf;
static s8 ep_reg_idx;
+static void *ipa_ipc_low_buff;
static ssize_t ipa3_read_gen_reg(struct file *file, char __user *ubuf,
@@ -1778,22 +1779,20 @@ static ssize_t ipa3_enable_ipc_low(struct file *file,
if (kstrtos8(dbg_buff, 0, &option))
return -EFAULT;
+ mutex_lock(&ipa3_ctx->lock);
if (option) {
- if (!ipa3_ctx->logbuf_low) {
- ipa3_ctx->logbuf_low =
+ if (!ipa_ipc_low_buff) {
+ ipa_ipc_low_buff =
ipc_log_context_create(IPA_IPC_LOG_PAGES,
"ipa_low", 0);
}
-
- if (ipa3_ctx->logbuf_low == NULL) {
- IPAERR("failed to get logbuf_low\n");
- return -EFAULT;
- }
+ if (ipa_ipc_low_buff == NULL)
+ IPAERR("failed to get logbuf_low\n");
+ ipa3_ctx->logbuf_low = ipa_ipc_low_buff;
} else {
- if (ipa3_ctx->logbuf_low)
- ipc_log_context_destroy(ipa3_ctx->logbuf_low);
ipa3_ctx->logbuf_low = NULL;
}
+ mutex_unlock(&ipa3_ctx->lock);
return count;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 8ec0974711a4..c5b56f16788a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -4055,6 +4055,7 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
if (!gsi_channel_props.ring_base_vaddr) {
IPAERR("fail to dma alloc %u bytes\n",
gsi_channel_props.ring_len);
+ result = -ENOMEM;
goto fail_alloc_channel_ring;
}
gsi_channel_props.ring_base_addr = dma_addr;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 0cf77bbde496..ac7ef6a21952 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1027,6 +1027,11 @@ struct ipa_tz_unlock_reg_info {
u32 size;
};
+struct ipa_dma_task_info {
+ struct ipa_mem_buffer mem;
+ struct ipahal_imm_cmd_pyld *cmd_pyld;
+};
+
/**
* struct ipa3_context - IPA context
* @class: pointer to the struct class
@@ -1246,6 +1251,7 @@ struct ipa3_context {
struct ipa3_smp2p_info smp2p_info;
u32 ipa_tz_unlock_reg_num;
struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
+ struct ipa_dma_task_info dma_task_info;
};
/**
@@ -2053,4 +2059,6 @@ int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
struct dentry *ipa_debugfs_get_root(void);
bool ipa3_is_msm_device(void);
struct device *ipa3_get_pdev(void);
+int ipa3_allocate_dma_task_for_gsi(void);
+void ipa3_free_dma_task_for_gsi(void);
#endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index ac7e57f10062..7212ba2a165c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1438,6 +1438,10 @@ int ipa3_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup)
mutex_lock(&ipa3_ctx->lock);
entry = __ipa3_find_rt_tbl(lookup->ip, lookup->name);
if (entry && entry->cookie == IPA_COOKIE) {
+ if (entry->ref_cnt == U32_MAX) {
+ IPAERR("fail: ref count crossed limit\n");
+ goto ret;
+ }
entry->ref_cnt++;
lookup->hdl = entry->id;
@@ -1447,6 +1451,8 @@ int ipa3_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup)
result = 0;
}
+
+ret:
mutex_unlock(&ipa3_ctx->lock);
return result;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index c8ff06ddda87..f4a7319ca290 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -3430,6 +3430,7 @@ static void ipa3_gsi_poll_after_suspend(struct ipa3_ep_context *ep)
/* queue a work to start polling if don't have one */
atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1);
if (!atomic_read(&ep->sys->curr_polling_state)) {
+ ipa3_inc_acquire_wakelock();
atomic_set(&ep->sys->curr_polling_state, 1);
queue_work(ep->sys->wq, &ep->sys->work);
}
@@ -3482,6 +3483,51 @@ void ipa3_suspend_apps_pipes(bool suspend)
}
}
+int ipa3_allocate_dma_task_for_gsi(void)
+{
+ struct ipahal_imm_cmd_dma_task_32b_addr cmd = { 0 };
+
+ IPADBG("Allocate mem\n");
+ ipa3_ctx->dma_task_info.mem.size = IPA_GSI_CHANNEL_STOP_PKT_SIZE;
+ ipa3_ctx->dma_task_info.mem.base = dma_alloc_coherent(ipa3_ctx->pdev,
+ ipa3_ctx->dma_task_info.mem.size,
+ &ipa3_ctx->dma_task_info.mem.phys_base,
+ GFP_KERNEL);
+ if (!ipa3_ctx->dma_task_info.mem.base) {
+ IPAERR("no mem\n");
+ return -EFAULT;
+ }
+
+ cmd.flsh = 1;
+ cmd.size1 = ipa3_ctx->dma_task_info.mem.size;
+ cmd.addr1 = ipa3_ctx->dma_task_info.mem.phys_base;
+ cmd.packet_size = ipa3_ctx->dma_task_info.mem.size;
+ ipa3_ctx->dma_task_info.cmd_pyld = ipahal_construct_imm_cmd(
+ IPA_IMM_CMD_DMA_TASK_32B_ADDR, &cmd, false);
+ if (!ipa3_ctx->dma_task_info.cmd_pyld) {
+ IPAERR("failed to construct dma_task_32b_addr cmd\n");
+ dma_free_coherent(ipa3_ctx->pdev,
+ ipa3_ctx->dma_task_info.mem.size,
+ ipa3_ctx->dma_task_info.mem.base,
+ ipa3_ctx->dma_task_info.mem.phys_base);
+ memset(&ipa3_ctx->dma_task_info, 0,
+ sizeof(ipa3_ctx->dma_task_info));
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+void ipa3_free_dma_task_for_gsi(void)
+{
+ dma_free_coherent(ipa3_ctx->pdev,
+ ipa3_ctx->dma_task_info.mem.size,
+ ipa3_ctx->dma_task_info.mem.base,
+ ipa3_ctx->dma_task_info.mem.phys_base);
+ ipahal_destroy_imm_cmd(ipa3_ctx->dma_task_info.cmd_pyld);
+ memset(&ipa3_ctx->dma_task_info, 0, sizeof(ipa3_ctx->dma_task_info));
+}
+
/**
* ipa3_inject_dma_task_for_gsi()- Send DMA_TASK to IPA for GSI stop channel
*
@@ -3490,41 +3536,12 @@ void ipa3_suspend_apps_pipes(bool suspend)
*/
int ipa3_inject_dma_task_for_gsi(void)
{
- static struct ipa_mem_buffer mem = {0};
- struct ipahal_imm_cmd_dma_task_32b_addr cmd = {0};
- static struct ipahal_imm_cmd_pyld *cmd_pyld;
struct ipa3_desc desc = {0};
- /* allocate the memory only for the very first time */
- if (!mem.base) {
- IPADBG("Allocate mem\n");
- mem.size = IPA_GSI_CHANNEL_STOP_PKT_SIZE;
- mem.base = dma_alloc_coherent(ipa3_ctx->pdev,
- mem.size,
- &mem.phys_base,
- GFP_KERNEL);
- if (!mem.base) {
- IPAERR("no mem\n");
- return -EFAULT;
- }
- }
- if (!cmd_pyld) {
- cmd.flsh = 1;
- cmd.size1 = mem.size;
- cmd.addr1 = mem.phys_base;
- cmd.packet_size = mem.size;
- cmd_pyld = ipahal_construct_imm_cmd(
- IPA_IMM_CMD_DMA_TASK_32B_ADDR, &cmd, false);
- if (!cmd_pyld) {
- IPAERR("failed to construct dma_task_32b_addr cmd\n");
- return -EFAULT;
- }
- }
-
desc.opcode = ipahal_imm_cmd_get_opcode_param(
IPA_IMM_CMD_DMA_TASK_32B_ADDR, 1);
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
+ desc.pyld = ipa3_ctx->dma_task_info.cmd_pyld->data;
+ desc.len = ipa3_ctx->dma_task_info.cmd_pyld->len;
desc.type = IPA_IMM_CMD_DESC;
IPADBG("sending 1B packet to IPA\n");
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 6731150ce4e7..258c0861ed52 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1663,6 +1663,9 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
extend_ioctl_data.u.rmnet_mux_val.vchannel_name,
sizeof(mux_channel[rmnet_index]
.vchannel_name));
+ mux_channel[rmnet_index].vchannel_name[
+ IFNAMSIZ - 1] = '\0';
+
IPAWANDBG("cashe device[%s:%d] in IPA_wan[%d]\n",
mux_channel[rmnet_index].vchannel_name,
mux_channel[rmnet_index].mux_id,
@@ -2365,32 +2368,41 @@ static int rmnet_ipa_ap_suspend(struct device *dev)
{
struct net_device *netdev = IPA_NETDEV();
struct ipa3_wwan_private *wwan_ptr;
+ int ret;
+
+ IPAWANDBG("Enter...\n");
- IPAWANDBG_LOW("Enter...\n");
if (netdev == NULL) {
IPAWANERR("netdev is NULL.\n");
- return 0;
+ ret = 0;
+ goto bail;
}
+ netif_tx_lock_bh(netdev);
wwan_ptr = netdev_priv(netdev);
if (wwan_ptr == NULL) {
IPAWANERR("wwan_ptr is NULL.\n");
- return 0;
+ ret = 0;
+ goto unlock_and_bail;
}
/* Do not allow A7 to suspend in case there are oustanding packets */
if (atomic_read(&wwan_ptr->outstanding_pkts) != 0) {
IPAWANDBG("Outstanding packets, postponing AP suspend.\n");
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto unlock_and_bail;
}
/* Make sure that there is no Tx operation ongoing */
- netif_tx_lock_bh(netdev);
+ netif_stop_queue(netdev);
ipa_rm_release_resource(IPA_RM_RESOURCE_WWAN_0_PROD);
- netif_tx_unlock_bh(netdev);
- IPAWANDBG_LOW("Exit\n");
+ ret = 0;
- return 0;
+unlock_and_bail:
+ netif_tx_unlock_bh(netdev);
+bail:
+ IPAWANDBG("Exit with %d\n", ret);
+ return ret;
}
/**
@@ -2407,10 +2419,10 @@ static int rmnet_ipa_ap_resume(struct device *dev)
{
struct net_device *netdev = IPA_NETDEV();
- IPAWANDBG_LOW("Enter...\n");
+ IPAWANDBG("Enter...\n");
if (netdev)
netif_wake_queue(netdev);
- IPAWANDBG_LOW("Exit\n");
+ IPAWANDBG("Exit\n");
return 0;
}
diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h
index 60e02fcb5e4b..b4f3df4ec3d2 100644
--- a/drivers/platform/msm/mhi/mhi.h
+++ b/drivers/platform/msm/mhi/mhi.h
@@ -338,9 +338,8 @@ struct db_mode {
u32 db_mode : 1;
enum MHI_BRSTMODE brstmode;
void (*process_db)(struct mhi_device_ctxt *mhi_dev_ctxt,
- void __iomem *io_addr,
- uintptr_t chan,
- u32 val);
+ void __iomem *io_addr, unsigned int chan,
+ dma_addr_t val);
};
struct mhi_ring {
@@ -728,15 +727,13 @@ int mhi_set_bus_request(struct mhi_device_ctxt *mhi_dev_ctxt,
int index);
int start_chan_sync(struct mhi_client_handle *client_handle);
void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
- void __iomem *io_addr,
- uintptr_t chan,
- u32 val);
+ void __iomem *io_addr, unsigned int chan,
+ dma_addr_t val);
void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt,
- void __iomem *io_addr,
- uintptr_t chan,
- u32 val);
+ void __iomem *io_addr, unsigned int chan,
+ dma_addr_t val);
void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr,
- uintptr_t io_offset, u32 val);
+ unsigned int chan, dma_addr_t val);
void mhi_reg_write_field(struct mhi_device_ctxt *mhi_dev_ctxt,
void __iomem *io_addr,
uintptr_t io_offset,
diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c
index 46baf7332900..78aa1beb870d 100644
--- a/drivers/platform/msm/mhi/mhi_main.c
+++ b/drivers/platform/msm/mhi/mhi_main.c
@@ -90,8 +90,9 @@ dma_pool_error:
}
static void mhi_write_db(struct mhi_device_ctxt *mhi_dev_ctxt,
- void __iomem *io_addr_lower,
- uintptr_t chan, u64 val)
+ void __iomem *io_addr_lower,
+ unsigned int chan,
+ dma_addr_t val)
{
uintptr_t io_offset = chan * sizeof(u64);
void __iomem *io_addr_upper =
@@ -1918,8 +1919,8 @@ EXPORT_SYMBOL(mhi_xfer_rddm);
void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
void __iomem *io_addr,
- uintptr_t chan,
- u32 val)
+ unsigned int chan,
+ dma_addr_t val)
{
struct mhi_ring *ring_ctxt =
&mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
@@ -1932,7 +1933,7 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_local_event_ctxt[chan];
mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ "db.set addr: %p io_offset %u val:0x%llx\n",
io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
@@ -1942,7 +1943,7 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
ring_ctxt->db_mode.db_mode = 0;
} else {
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
- "Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n",
+ "Not ringing xfer db, chan %u, brstmode %d db_mode %d\n",
chan, ring_ctxt->db_mode.brstmode,
ring_ctxt->db_mode.db_mode);
}
@@ -1950,23 +1951,24 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt,
void __iomem *io_addr,
- uintptr_t chan,
- u32 val)
+ unsigned int chan,
+ dma_addr_t val)
{
mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ "db.set addr: %p io_offset %u val:0x%llx\n",
io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
mhi_write_db(mhi_dev_ctxt, io_addr, chan, val);
}
void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt,
- void __iomem *io_addr,
- uintptr_t chan, u32 val)
+ void __iomem *io_addr,
+ unsigned int chan,
+ dma_addr_t val)
{
mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ "db.set addr: %p io_offset %u val:0x%llx\n",
io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
@@ -1981,7 +1983,7 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt,
chan_ctxt->db_mode.db_mode = 0;
} else {
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
- "Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n",
+ "Not ringing xfer db, chan %u, brstmode %d db_mode %d\n",
chan, chan_ctxt->db_mode.brstmode,
chan_ctxt->db_mode.db_mode);
}
diff --git a/drivers/platform/msm/mhi/mhi_ssr.c b/drivers/platform/msm/mhi/mhi_ssr.c
index 9f18b1e7ef85..f4f2e427972f 100644
--- a/drivers/platform/msm/mhi/mhi_ssr.c
+++ b/drivers/platform/msm/mhi/mhi_ssr.c
@@ -29,11 +29,9 @@ static int mhi_ssr_notify_cb(struct notifier_block *nb,
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received ESOC notifcation:%lu crashed:%d\n", action, crashed);
switch (action) {
- case SUBSYS_BEFORE_SHUTDOWN:
- /*
- * update internal states only, we'll clean up MHI context
- * after device shutdown completely.
- */
+ case SUBSYS_AFTER_SHUTDOWN:
+
+ /* Disable internal state, no more communication */
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
cur_state = mhi_tryset_pm_state(mhi_dev_ctxt,
MHI_PM_LD_ERR_FATAL_DETECT);
@@ -42,8 +40,6 @@ static int mhi_ssr_notify_cb(struct notifier_block *nb,
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Failed to transition to state 0x%x from 0x%x\n",
MHI_PM_LD_ERR_FATAL_DETECT, cur_state);
- break;
- case SUBSYS_AFTER_SHUTDOWN:
if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE)
process_disable_transition(MHI_PM_SHUTDOWN_PROCESS,
mhi_dev_ctxt);
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index c6009d767db5..f1e348969c7b 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -643,6 +643,9 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx)
int rc;
int force_pt_coherent = 1;
int smmu_bypass = !ctx->smmu_s1_en;
+ dma_addr_t iova_base = 0;
+ dma_addr_t iova_end = ctx->smmu_base + ctx->smmu_size - 1;
+ struct iommu_domain_geometry geometry;
if (!ctx->use_smmu)
return 0;
@@ -700,6 +703,17 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx)
rc);
goto release_mapping;
}
+ memset(&geometry, 0, sizeof(geometry));
+ geometry.aperture_start = iova_base;
+ geometry.aperture_end = iova_end;
+ rc = iommu_domain_set_attr(ctx->mapping->domain,
+ DOMAIN_ATTR_GEOMETRY,
+ &geometry);
+ if (rc) {
+ dev_err(ctx->dev, "Set geometry attribute to SMMU failed (%d)\n",
+ rc);
+ goto release_mapping;
+ }
}
}
@@ -822,7 +836,6 @@ static void msm_11ad_ssr_deinit(struct msm11ad_ctx *ctx)
ctx->ramdump_dev = NULL;
}
- kfree(ctx->ramdump_addr);
ctx->ramdump_addr = NULL;
if (ctx->subsys_handle) {
@@ -855,6 +868,14 @@ static int msm_11ad_ssr_init(struct msm11ad_ctx *ctx)
goto out_rc;
}
+ ctx->ramdump_dev = create_ramdump_device(ctx->subsysdesc.name,
+ ctx->subsysdesc.dev);
+ if (!ctx->ramdump_dev) {
+ dev_err(ctx->dev, "Create ramdump device failed\n");
+ rc = -ENOMEM;
+ goto out_rc;
+ }
+
/* register ramdump area */
ctx->ramdump_addr = kmalloc(WIGIG_RAMDUMP_SIZE, GFP_KERNEL);
if (!ctx->ramdump_addr) {
@@ -864,25 +885,19 @@ static int msm_11ad_ssr_init(struct msm11ad_ctx *ctx)
ctx->dump_data.addr = virt_to_phys(ctx->ramdump_addr);
ctx->dump_data.len = WIGIG_RAMDUMP_SIZE;
+ strlcpy(ctx->dump_data.name, "KWIGIG",
+ sizeof(ctx->dump_data.name));
dump_entry.id = MSM_DUMP_DATA_WIGIG;
dump_entry.addr = virt_to_phys(&ctx->dump_data);
rc = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
if (rc) {
dev_err(ctx->dev, "Dump table setup failed: %d\n", rc);
- goto out_rc;
- }
-
- ctx->ramdump_dev = create_ramdump_device(ctx->subsysdesc.name,
- ctx->subsysdesc.dev);
- if (!ctx->ramdump_dev) {
- dev_err(ctx->dev, "Create ramdump device failed: %d\n", rc);
- rc = -ENOMEM;
+ kfree(ctx->ramdump_addr);
goto out_rc;
}
return 0;
-
out_rc:
msm_11ad_ssr_deinit(ctx);
return rc;
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index a30ed90d6e92..8d038ba0770d 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -33,6 +33,7 @@
#include <soc/qcom/scm.h>
#include <soc/qcom/restart.h>
#include <soc/qcom/watchdog.h>
+#include <soc/qcom/minidump.h>
#define EMERGENCY_DLOAD_MAGIC1 0x322A4F99
#define EMERGENCY_DLOAD_MAGIC2 0xC67E4350
@@ -42,9 +43,10 @@
#define SCM_IO_DISABLE_PMIC_ARBITER 1
#define SCM_IO_DEASSERT_PS_HOLD 2
#define SCM_WDOG_DEBUG_BOOT_PART 0x9
-#define SCM_DLOAD_MODE 0X10
+#define SCM_DLOAD_FULLDUMP 0X10
#define SCM_EDLOAD_MODE 0X01
#define SCM_DLOAD_CMD 0x10
+#define SCM_DLOAD_MINIDUMP 0X20
static int restart_mode;
@@ -69,6 +71,7 @@ static void scm_disable_sdi(void);
#endif
static int in_panic;
+static int dload_type = SCM_DLOAD_FULLDUMP;
static int download_mode = 1;
static struct kobject dload_kobj;
static void *dload_mode_addr, *dload_type_addr;
@@ -142,7 +145,7 @@ static void set_dload_mode(int on)
mb();
}
- ret = scm_set_dload_mode(on ? SCM_DLOAD_MODE : 0, 0);
+ ret = scm_set_dload_mode(on ? dload_type : 0, 0);
if (ret)
pr_err("Failed to set secure DLOAD mode: %d\n", ret);
@@ -185,7 +188,6 @@ static int dload_set(const char *val, struct kernel_param *kp)
int old_val = download_mode;
ret = param_set_int(val, kp);
-
if (ret)
return ret;
@@ -454,7 +456,7 @@ static ssize_t show_emmc_dload(struct kobject *kobj, struct attribute *attr,
else
show_val = 0;
- return snprintf(buf, sizeof(show_val), "%u\n", show_val);
+ return scnprintf(buf, sizeof(show_val), "%u\n", show_val);
}
static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr,
@@ -477,10 +479,50 @@ static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr,
return count;
}
+
+#ifdef CONFIG_QCOM_MINIDUMP
+
+static DEFINE_MUTEX(tcsr_lock);
+
+static ssize_t show_dload_mode(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "DLOAD dump type: %s\n",
+ (dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full");
+}
+
+static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ if (sysfs_streq(buf, "full")) {
+ dload_type = SCM_DLOAD_FULLDUMP;
+ } else if (sysfs_streq(buf, "mini")) {
+ if (!msm_minidump_enabled()) {
+ pr_info("Minidump is not enabled\n");
+ return -ENODEV;
+ }
+ dload_type = SCM_DLOAD_MINIDUMP;
+ } else {
+ pr_info("Invalid value. Use 'full' or 'mini'\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&tcsr_lock);
+ /*Overwrite TCSR reg*/
+ set_dload_mode(dload_type);
+ mutex_unlock(&tcsr_lock);
+ return count;
+}
+RESET_ATTR(dload_mode, 0644, show_dload_mode, store_dload_mode);
+#endif
+
RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload);
static struct attribute *reset_attrs[] = {
&reset_attr_emmc_dload.attr,
+#ifdef CONFIG_QCOM_MINIDUMP
+ &reset_attr_dload_mode.attr,
+#endif
NULL
};
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 67f9d4fafeb8..539e757d3e99 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -434,23 +434,28 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data,
return rc;
}
- split_fcc(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua);
- pval.intval = slave_fcc_ua;
- rc = power_supply_set_property(chip->pl_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- if (rc < 0) {
- pr_err("Couldn't set parallel fcc, rc=%d\n", rc);
- return rc;
- }
+ if (chip->pl_mode != POWER_SUPPLY_PL_NONE) {
+ split_fcc(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua);
+
+ pval.intval = slave_fcc_ua;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set parallel fcc, rc=%d\n", rc);
+ return rc;
+ }
- chip->slave_fcc_ua = slave_fcc_ua;
+ chip->slave_fcc_ua = slave_fcc_ua;
- pval.intval = master_fcc_ua;
- rc = power_supply_set_property(chip->main_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- if (rc < 0) {
- pr_err("Could not set main fcc, rc=%d\n", rc);
- return rc;
+ pval.intval = master_fcc_ua;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Could not set main fcc, rc=%d\n", rc);
+ return rc;
+ }
}
pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
@@ -685,9 +690,6 @@ static bool is_main_available(struct pl_data *chip)
chip->main_psy = power_supply_get_by_name("main");
- if (chip->main_psy)
- rerun_election(chip->usb_icl_votable);
-
return !!chip->main_psy;
}
@@ -866,7 +868,18 @@ static void status_change_work(struct work_struct *work)
struct pl_data *chip = container_of(work,
struct pl_data, status_change_work);
- if (!is_main_available(chip))
+ if (!chip->main_psy && is_main_available(chip)) {
+ /*
+ * re-run election for FCC/FV/ICL once main_psy
+ * is available to ensure all votes are reflected
+ * on hardware
+ */
+ rerun_election(chip->usb_icl_votable);
+ rerun_election(chip->fcc_votable);
+ rerun_election(chip->fv_votable);
+ }
+
+ if (!chip->main_psy)
return;
if (!is_batt_available(chip))
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 316313ff6408..25c9d0251cf1 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -128,11 +128,6 @@ enum fg_irq_index {
FG_IRQ_MAX,
};
-/* WA flags */
-enum {
- DELTA_SOC_IRQ_WA = BIT(0),
-};
-
/*
* List of FG_SRAM parameters. Please add a parameter only if it is an entry
* that will be used either to configure an entity (e.g. termination current)
@@ -152,6 +147,7 @@ enum fg_sram_param_id {
FG_SRAM_CC_SOC,
FG_SRAM_CC_SOC_SW,
FG_SRAM_ACT_BATT_CAP,
+ FG_SRAM_TIMEBASE,
/* Entries below here are configurable during initialization */
FG_SRAM_CUTOFF_VOLT,
FG_SRAM_EMPTY_VOLT,
@@ -165,12 +161,14 @@ enum fg_sram_param_id {
FG_SRAM_ESR_PULSE_THRESH,
FG_SRAM_SYS_TERM_CURR,
FG_SRAM_CHG_TERM_CURR,
+ FG_SRAM_CHG_TERM_BASE_CURR,
FG_SRAM_DELTA_MSOC_THR,
FG_SRAM_DELTA_BSOC_THR,
FG_SRAM_RECHARGE_SOC_THR,
FG_SRAM_RECHARGE_VBATT_THR,
FG_SRAM_KI_COEFF_MED_DISCHG,
FG_SRAM_KI_COEFF_HI_DISCHG,
+ FG_SRAM_KI_COEFF_FULL_SOC,
FG_SRAM_ESR_TIGHT_FILTER,
FG_SRAM_ESR_BROAD_FILTER,
FG_SRAM_SLOPE_LIMIT,
@@ -210,6 +208,7 @@ struct fg_alg_flag {
enum wa_flags {
PMI8998_V1_REV_WA = BIT(0),
+ PM660_TSMC_OSC_WA = BIT(1),
};
enum slope_limit_status {
@@ -229,6 +228,7 @@ struct fg_dt_props {
int empty_volt_mv;
int vbatt_low_thr_mv;
int chg_term_curr_ma;
+ int chg_term_base_curr_ma;
int sys_term_curr_ma;
int delta_soc_thr;
int recharge_soc_thr;
@@ -322,6 +322,23 @@ static const struct fg_pt fg_ln_table[] = {
{ 128000, 4852 },
};
+/* each tuple is - <temperature in degC, Timebase> */
+static const struct fg_pt fg_tsmc_osc_table[] = {
+ { -20, 395064 },
+ { -10, 398114 },
+ { 0, 401669 },
+ { 10, 404641 },
+ { 20, 408856 },
+ { 25, 412449 },
+ { 30, 416532 },
+ { 40, 420289 },
+ { 50, 425020 },
+ { 60, 430160 },
+ { 70, 434175 },
+ { 80, 439475 },
+ { 90, 444992 },
+};
+
struct fg_chip {
struct device *dev;
struct pmic_revid_data *pmic_rev_id;
@@ -333,6 +350,7 @@ struct fg_chip {
struct power_supply *dc_psy;
struct power_supply *parallel_psy;
struct iio_channel *batt_id_chan;
+ struct iio_channel *die_temp_chan;
struct fg_memif *sram;
struct fg_irq_info *irqs;
struct votable *awake_votable;
@@ -356,6 +374,7 @@ struct fg_chip {
u32 rradc_base;
u32 wa_flags;
int batt_id_ohms;
+ int ki_coeff_full_soc;
int charge_status;
int prev_charge_status;
int charge_done;
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 2e9425040f72..4368709118ac 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -47,11 +47,14 @@
#define ESR_UPD_TIGHT_LOW_TEMP_OFFSET 2
#define ESR_UPD_BROAD_LOW_TEMP_OFFSET 3
#define KI_COEFF_MED_DISCHG_WORD 9
+#define TIMEBASE_OFFSET 1
#define KI_COEFF_MED_DISCHG_OFFSET 3
#define KI_COEFF_HI_DISCHG_WORD 10
#define KI_COEFF_HI_DISCHG_OFFSET 0
#define KI_COEFF_LOW_DISCHG_WORD 10
#define KI_COEFF_LOW_DISCHG_OFFSET 2
+#define KI_COEFF_FULL_SOC_WORD 12
+#define KI_COEFF_FULL_SOC_OFFSET 2
#define DELTA_MSOC_THR_WORD 12
#define DELTA_MSOC_THR_OFFSET 3
#define DELTA_BSOC_THR_WORD 13
@@ -128,6 +131,7 @@
#define RECHARGE_SOC_THR_v2_WORD 14
#define RECHARGE_SOC_THR_v2_OFFSET 1
#define CHG_TERM_CURR_v2_WORD 15
+#define CHG_TERM_BASE_CURR_v2_OFFSET 0
#define CHG_TERM_CURR_v2_OFFSET 1
#define EMPTY_VOLT_v2_WORD 15
#define EMPTY_VOLT_v2_OFFSET 3
@@ -226,6 +230,9 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = {
PARAM(KI_COEFF_HI_DISCHG, KI_COEFF_HI_DISCHG_WORD,
KI_COEFF_HI_DISCHG_OFFSET, 1, 1000, 244141, 0,
fg_encode_default, NULL),
+ PARAM(KI_COEFF_FULL_SOC, KI_COEFF_FULL_SOC_WORD,
+ KI_COEFF_FULL_SOC_OFFSET, 1, 1000, 244141, 0,
+ fg_encode_default, NULL),
PARAM(ESR_TIGHT_FILTER, ESR_FILTER_WORD, ESR_UPD_TIGHT_OFFSET,
1, 512, 1000000, 0, fg_encode_default, NULL),
PARAM(ESR_BROAD_FILTER, ESR_FILTER_WORD, ESR_UPD_BROAD_OFFSET,
@@ -255,6 +262,8 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = {
fg_decode_cc_soc),
PARAM(ACT_BATT_CAP, ACT_BATT_CAP_BKUP_WORD, ACT_BATT_CAP_BKUP_OFFSET, 2,
1, 1, 0, NULL, fg_decode_default),
+ PARAM(TIMEBASE, KI_COEFF_MED_DISCHG_WORD, TIMEBASE_OFFSET, 2, 1000,
+ 61000, 0, fg_encode_default, NULL),
/* Entries below here are configurable during initialization */
PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000,
244141, 0, fg_encode_voltage, NULL),
@@ -270,6 +279,9 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = {
1000000, 122070, 0, fg_encode_current, NULL),
PARAM(CHG_TERM_CURR, CHG_TERM_CURR_v2_WORD, CHG_TERM_CURR_v2_OFFSET, 1,
100000, 390625, 0, fg_encode_current, NULL),
+ PARAM(CHG_TERM_BASE_CURR, CHG_TERM_CURR_v2_WORD,
+ CHG_TERM_BASE_CURR_v2_OFFSET, 1, 1024, 1000, 0,
+ fg_encode_current, NULL),
PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_v2_WORD, DELTA_MSOC_THR_v2_OFFSET,
1, 2048, 100, 0, fg_encode_default, NULL),
PARAM(DELTA_BSOC_THR, DELTA_BSOC_THR_v2_WORD, DELTA_BSOC_THR_v2_OFFSET,
@@ -298,6 +310,9 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = {
PARAM(KI_COEFF_HI_DISCHG, KI_COEFF_HI_DISCHG_v2_WORD,
KI_COEFF_HI_DISCHG_v2_OFFSET, 1, 1000, 244141, 0,
fg_encode_default, NULL),
+ PARAM(KI_COEFF_FULL_SOC, KI_COEFF_FULL_SOC_WORD,
+ KI_COEFF_FULL_SOC_OFFSET, 1, 1000, 244141, 0,
+ fg_encode_default, NULL),
PARAM(ESR_TIGHT_FILTER, ESR_FILTER_WORD, ESR_UPD_TIGHT_OFFSET,
1, 512, 1000000, 0, fg_encode_default, NULL),
PARAM(ESR_BROAD_FILTER, ESR_FILTER_WORD, ESR_UPD_BROAD_OFFSET,
@@ -1482,6 +1497,37 @@ static int fg_adjust_ki_coeff_dischg(struct fg_chip *chip)
return 0;
}
+#define KI_COEFF_FULL_SOC_DEFAULT 733
+static int fg_adjust_ki_coeff_full_soc(struct fg_chip *chip, int batt_temp)
+{
+ int rc, ki_coeff_full_soc;
+ u8 val;
+
+ if (batt_temp < 0)
+ ki_coeff_full_soc = 0;
+ else
+ ki_coeff_full_soc = KI_COEFF_FULL_SOC_DEFAULT;
+
+ if (chip->ki_coeff_full_soc == ki_coeff_full_soc)
+ return 0;
+
+ fg_encode(chip->sp, FG_SRAM_KI_COEFF_FULL_SOC, ki_coeff_full_soc, &val);
+ rc = fg_sram_write(chip,
+ chip->sp[FG_SRAM_KI_COEFF_FULL_SOC].addr_word,
+ chip->sp[FG_SRAM_KI_COEFF_FULL_SOC].addr_byte, &val,
+ chip->sp[FG_SRAM_KI_COEFF_FULL_SOC].len,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing ki_coeff_full_soc, rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->ki_coeff_full_soc = ki_coeff_full_soc;
+ fg_dbg(chip, FG_STATUS, "Wrote ki_coeff_full_soc %d\n",
+ ki_coeff_full_soc);
+ return 0;
+}
+
static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv)
{
u8 buf;
@@ -1924,7 +1970,7 @@ static int fg_esr_fcc_config(struct fg_chip *chip)
{
union power_supply_propval prop = {0, };
int rc;
- bool parallel_en = false;
+ bool parallel_en = false, qnovo_en = false;
if (is_parallel_charger_available(chip)) {
rc = power_supply_get_property(chip->parallel_psy,
@@ -1937,19 +1983,25 @@ static int fg_esr_fcc_config(struct fg_chip *chip)
parallel_en = prop.intval;
}
- fg_dbg(chip, FG_POWER_SUPPLY, "charge_status: %d parallel_en: %d esr_fcc_ctrl_en: %d\n",
- chip->charge_status, parallel_en, chip->esr_fcc_ctrl_en);
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &prop);
+ if (!rc)
+ qnovo_en = prop.intval;
+
+ fg_dbg(chip, FG_POWER_SUPPLY, "chg_sts: %d par_en: %d qnov_en: %d esr_fcc_ctrl_en: %d\n",
+ chip->charge_status, parallel_en, qnovo_en,
+ chip->esr_fcc_ctrl_en);
if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
- parallel_en) {
+ (parallel_en || qnovo_en)) {
if (chip->esr_fcc_ctrl_en)
return 0;
/*
- * When parallel charging is enabled, configure ESR FCC to
- * 300mA to trigger an ESR pulse. Without this, FG can ask
- * the main charger to increase FCC when it is supposed to
- * decrease it.
+ * When parallel charging or Qnovo is enabled, configure ESR
+ * FCC to 300mA to trigger an ESR pulse. Without this, FG can
+ * request the main charger to increase FCC when it is supposed
+ * to decrease it.
*/
rc = fg_masked_write(chip, BATT_INFO_ESR_FAST_CRG_CFG(chip),
ESR_FAST_CRG_IVAL_MASK |
@@ -1968,8 +2020,8 @@ static int fg_esr_fcc_config(struct fg_chip *chip)
/*
* If we're here, then it means either the device is not in
- * charging state or parallel charging is disabled. Disable
- * ESR fast charge current control in SW.
+ * charging state or parallel charging / Qnovo is disabled.
+ * Disable ESR fast charge current control in SW.
*/
rc = fg_masked_write(chip, BATT_INFO_ESR_FAST_CRG_CFG(chip),
ESR_FAST_CRG_CTL_EN_BIT, 0);
@@ -2066,6 +2118,11 @@ static void status_change_work(struct work_struct *work)
if (rc < 0)
pr_err("Error in configuring slope limiter rc:%d\n",
rc);
+
+ rc = fg_adjust_ki_coeff_full_soc(chip, batt_temp);
+ if (rc < 0)
+ pr_err("Error in configuring ki_coeff_full_soc rc:%d\n",
+ rc);
}
fg_batt_avg_update(chip);
@@ -3101,6 +3158,21 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
+ if (!(chip->wa_flags & PMI8998_V1_REV_WA)) {
+ fg_encode(chip->sp, FG_SRAM_CHG_TERM_BASE_CURR,
+ chip->dt.chg_term_base_curr_ma, buf);
+ rc = fg_sram_write(chip,
+ chip->sp[FG_SRAM_CHG_TERM_BASE_CURR].addr_word,
+ chip->sp[FG_SRAM_CHG_TERM_BASE_CURR].addr_byte,
+ buf, chip->sp[FG_SRAM_CHG_TERM_BASE_CURR].len,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing chg_term_base_curr, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
if (chip->dt.vbatt_low_thr_mv > 0) {
fg_encode(chip->sp, FG_SRAM_VBATT_LOW,
chip->dt.vbatt_low_thr_mv, buf);
@@ -3302,6 +3374,40 @@ static int fg_memif_init(struct fg_chip *chip)
return fg_ima_init(chip);
}
+static int fg_adjust_timebase(struct fg_chip *chip)
+{
+ int rc = 0, die_temp;
+ s32 time_base = 0;
+ u8 buf[2] = {0};
+
+ if ((chip->wa_flags & PM660_TSMC_OSC_WA) && chip->die_temp_chan) {
+ rc = iio_read_channel_processed(chip->die_temp_chan, &die_temp);
+ if (rc < 0) {
+ pr_err("Error in reading die_temp, rc:%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_lerp(fg_tsmc_osc_table, ARRAY_SIZE(fg_tsmc_osc_table),
+ die_temp / 1000, &time_base);
+ if (rc < 0) {
+ pr_err("Error to lookup fg_tsmc_osc_table rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_encode(chip->sp, FG_SRAM_TIMEBASE, time_base, buf);
+ rc = fg_sram_write(chip,
+ chip->sp[FG_SRAM_TIMEBASE].addr_word,
+ chip->sp[FG_SRAM_TIMEBASE].addr_byte, buf,
+ chip->sp[FG_SRAM_TIMEBASE].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing timebase, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
/* INTERRUPT HANDLERS STAY HERE */
static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data)
@@ -3409,6 +3515,10 @@ static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data)
if (rc < 0)
pr_err("Error in configuring slope limiter rc:%d\n", rc);
+ rc = fg_adjust_ki_coeff_full_soc(chip, batt_temp);
+ if (rc < 0)
+ pr_err("Error in configuring ki_coeff_full_soc rc:%d\n", rc);
+
if (!batt_psy_initialized(chip)) {
chip->last_batt_temp = batt_temp;
return IRQ_HANDLED;
@@ -3419,6 +3529,10 @@ static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data)
chip->health = prop.intval;
if (chip->last_batt_temp != batt_temp) {
+ rc = fg_adjust_timebase(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting timebase, rc=%d\n", rc);
+
chip->last_batt_temp = batt_temp;
power_supply_changed(chip->batt_psy);
}
@@ -3488,6 +3602,10 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data)
if (rc < 0)
pr_err("Error in validating ESR, rc=%d\n", rc);
+ rc = fg_adjust_timebase(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting timebase, rc=%d\n", rc);
+
if (batt_psy_initialized(chip))
power_supply_changed(chip->batt_psy);
@@ -3755,6 +3873,7 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip)
#define DEFAULT_EMPTY_VOLT_MV 2800
#define DEFAULT_RECHARGE_VOLT_MV 4250
#define DEFAULT_CHG_TERM_CURR_MA 100
+#define DEFAULT_CHG_TERM_BASE_CURR_MA 75
#define DEFAULT_SYS_TERM_CURR_MA -125
#define DEFAULT_DELTA_SOC_THR 1
#define DEFAULT_RECHARGE_SOC_THR 95
@@ -3829,6 +3948,8 @@ static int fg_parse_dt(struct fg_chip *chip)
chip->sp = pmi8998_v2_sram_params;
chip->alg_flags = pmi8998_v2_alg_flags;
chip->use_ima_single_mode = true;
+ if (chip->pmic_rev_id->fab_id == PM660_FAB_ID_TSMC)
+ chip->wa_flags |= PM660_TSMC_OSC_WA;
break;
default:
return -EINVAL;
@@ -3909,6 +4030,12 @@ static int fg_parse_dt(struct fg_chip *chip)
else
chip->dt.sys_term_curr_ma = temp;
+ rc = of_property_read_u32(node, "qcom,fg-chg-term-base-current", &temp);
+ if (rc < 0)
+ chip->dt.chg_term_base_curr_ma = DEFAULT_CHG_TERM_BASE_CURR_MA;
+ else
+ chip->dt.chg_term_base_curr_ma = temp;
+
rc = of_property_read_u32(node, "qcom,fg-delta-soc-thr", &temp);
if (rc < 0)
chip->dt.delta_soc_thr = DEFAULT_DELTA_SOC_THR;
@@ -4147,6 +4274,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
chip->irqs = fg_irqs;
chip->charge_status = -EINVAL;
chip->prev_charge_status = -EINVAL;
+ chip->ki_coeff_full_soc = -EINVAL;
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
if (!chip->regmap) {
dev_err(chip->dev, "Parent regmap is unavailable\n");
@@ -4163,6 +4291,21 @@ static int fg_gen3_probe(struct platform_device *pdev)
return rc;
}
+ rc = of_property_match_string(chip->dev->of_node,
+ "io-channel-names", "rradc_die_temp");
+ if (rc >= 0) {
+ chip->die_temp_chan = iio_channel_get(chip->dev,
+ "rradc_die_temp");
+ if (IS_ERR(chip->die_temp_chan)) {
+ if (PTR_ERR(chip->die_temp_chan) != -EPROBE_DEFER)
+ pr_err("rradc_die_temp unavailable %ld\n",
+ PTR_ERR(chip->die_temp_chan));
+ rc = PTR_ERR(chip->die_temp_chan);
+ chip->die_temp_chan = NULL;
+ return rc;
+ }
+ }
+
chip->awake_votable = create_votable("FG_WS", VOTE_SET_ANY, fg_awake_cb,
chip);
if (IS_ERR(chip->awake_votable)) {
diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c
index c74dc8989821..eb97eb0ff2ac 100644
--- a/drivers/power/supply/qcom/qpnp-qnovo.c
+++ b/drivers/power/supply/qcom/qpnp-qnovo.c
@@ -89,7 +89,16 @@
#define QNOVO_STRM_CTRL 0xA8
#define QNOVO_IADC_OFFSET_OVR_VAL 0xA9
#define QNOVO_IADC_OFFSET_OVR 0xAA
+
#define QNOVO_DISABLE_CHARGING 0xAB
+#define ERR_SWITCHER_DISABLED BIT(7)
+#define ERR_JEITA_SOFT_CONDITION BIT(6)
+#define ERR_BAT_OV BIT(5)
+#define ERR_CV_MODE BIT(4)
+#define ERR_BATTERY_MISSING BIT(3)
+#define ERR_SAFETY_TIMER_EXPIRED BIT(2)
+#define ERR_CHARGING_DISABLED BIT(1)
+#define ERR_JEITA_HARD_CONDITION BIT(0)
#define QNOVO_TR_IADC_OFFSET_0 0xF1
#define QNOVO_TR_IADC_OFFSET_1 0xF2
@@ -1107,24 +1116,28 @@ static int qnovo_update_status(struct qnovo *chip)
{
u8 val = 0;
int rc;
- bool charging;
+ bool ok_to_qnovo;
bool changed = false;
rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
if (rc < 0) {
pr_err("Couldn't read error sts rc = %d\n", rc);
- charging = false;
+ ok_to_qnovo = false;
} else {
- charging = !(val & QNOVO_ERROR_CHARGING_DISABLED);
+ /*
+ * For CV mode keep qnovo enabled, userspace is expected to
+ * disable it after few runs
+ */
+ ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? true : false;
}
- if (chip->ok_to_qnovo ^ charging) {
+ if (chip->ok_to_qnovo ^ ok_to_qnovo) {
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !charging, 0);
- if (!charging)
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !ok_to_qnovo, 0);
+ if (!ok_to_qnovo)
vote(chip->disable_votable, USER_VOTER, true, 0);
- chip->ok_to_qnovo = charging;
+ chip->ok_to_qnovo = ok_to_qnovo;
changed = true;
}
@@ -1247,6 +1260,16 @@ static int qnovo_hw_init(struct qnovo *chip)
chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR;
chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000);
+ /* allow charger error conditions to disable qnovo, CV mode excluded */
+ val = ERR_SWITCHER_DISABLED | ERR_JEITA_SOFT_CONDITION | ERR_BAT_OV |
+ ERR_BATTERY_MISSING | ERR_SAFETY_TIMER_EXPIRED |
+ ERR_CHARGING_DISABLED | ERR_JEITA_HARD_CONDITION;
+ rc = qnovo_write(chip, QNOVO_DISABLE_CHARGING, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write QNOVO_DISABLE_CHARGING rc = %d\n", rc);
+ return rc;
+ }
+
return 0;
}
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 81e656c06da0..8855a1c74e0b 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -244,6 +244,8 @@ struct smb_dt_props {
int boost_threshold_ua;
int fv_uv;
int wipower_max_uw;
+ int min_freq_khz;
+ int max_freq_khz;
u32 step_soc_threshold[STEP_CHARGING_MAX_STEPS - 1];
s32 step_cc_delta[STEP_CHARGING_MAX_STEPS];
struct device_node *revid_dev_node;
@@ -338,6 +340,18 @@ static int smb2_parse_dt(struct smb2 *chip)
if (rc < 0)
chip->dt.boost_threshold_ua = MICRO_P1A;
+ rc = of_property_read_u32(node,
+ "qcom,min-freq-khz",
+ &chip->dt.min_freq_khz);
+ if (rc < 0)
+ chip->dt.min_freq_khz = -EINVAL;
+
+ rc = of_property_read_u32(node,
+ "qcom,max-freq-khz",
+ &chip->dt.max_freq_khz);
+ if (rc < 0)
+ chip->dt.max_freq_khz = -EINVAL;
+
rc = of_property_read_u32(node, "qcom,wipower-max-uw",
&chip->dt.wipower_max_uw);
if (rc < 0)
@@ -526,6 +540,12 @@ static int smb2_usb_set_prop(struct power_supply *psy,
struct smb_charger *chg = &chip->chg;
int rc = 0;
+ mutex_lock(&chg->lock);
+ if (!chg->typec_present) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
rc = smblib_set_prop_usb_voltage_min(chg, val);
@@ -564,6 +584,8 @@ static int smb2_usb_set_prop(struct power_supply *psy,
break;
}
+unlock:
+ mutex_unlock(&chg->lock);
return rc;
}
@@ -1336,10 +1358,12 @@ static int smb2_configure_typec(struct smb_charger *chg)
return rc;
}
- /* disable try.SINK mode */
- rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT, 0);
+ /* disable try.SINK mode and legacy cable IRQs */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT |
+ TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT |
+ TYPEC_LEGACY_CABLE_INT_EN_BIT, 0);
if (rc < 0) {
- dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc);
+ dev_err(chg->dev, "Couldn't set Type-C config rc=%d\n", rc);
return rc;
}
@@ -1438,6 +1462,16 @@ static int smb2_init_hw(struct smb2 *chip)
smblib_get_charge_param(chg, &chg->param.dc_icl,
&chip->dt.dc_icl_ua);
+ if (chip->dt.min_freq_khz > 0) {
+ chg->param.freq_buck.min_u = chip->dt.min_freq_khz;
+ chg->param.freq_boost.min_u = chip->dt.min_freq_khz;
+ }
+
+ if (chip->dt.max_freq_khz > 0) {
+ chg->param.freq_buck.max_u = chip->dt.max_freq_khz;
+ chg->param.freq_boost.max_u = chip->dt.max_freq_khz;
+ }
+
/* set a slower soft start setting for OTG */
rc = smblib_masked_write(chg, DC_ENG_SSUPPLY_CFG2_REG,
ENG_SSUPPLY_IVREF_OTG_SS_MASK, OTG_SS_SLOW);
@@ -1485,6 +1519,8 @@ static int smb2_init_hw(struct smb2 *chip)
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ true, 0);
vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
chip->dt.hvdcp_disable, 0);
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
@@ -2022,6 +2058,16 @@ static int smb2_request_interrupts(struct smb2 *chip)
return rc;
}
+static void smb2_disable_interrupts(struct smb_charger *chg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(smb2_irqs); i++) {
+ if (smb2_irqs[i].irq > 0)
+ disable_irq(smb2_irqs[i].irq);
+ }
+}
+
#if defined(CONFIG_DEBUG_FS)
static int force_batt_psy_update_write(void *data, u64 val)
@@ -2284,6 +2330,9 @@ static void smb2_shutdown(struct platform_device *pdev)
struct smb2 *chip = platform_get_drvdata(pdev);
struct smb_charger *chg = &chip->chg;
+ /* disable all interrupts */
+ smb2_disable_interrupts(chg);
+
/* configure power role for UFP */
smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_POWER_ROLE_CMD_MASK, UFP_EN_CMD_BIT);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index bd96703579f6..1e417e8aa22d 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -543,30 +543,6 @@ static void smblib_rerun_apsd(struct smb_charger *chg)
smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc);
}
-static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
-{
- const struct apsd_result *apsd_result;
-
- /*
- * PD_INACTIVE_VOTER on hvdcp_disable_votable indicates whether
- * apsd rerun was tried earlier
- */
- if (get_client_vote(chg->hvdcp_disable_votable_indirect,
- PD_INACTIVE_VOTER)) {
- vote(chg->hvdcp_disable_votable_indirect,
- PD_INACTIVE_VOTER, false, 0);
- /* ensure hvdcp is enabled */
- if (!get_effective_result(
- chg->hvdcp_disable_votable_indirect)) {
- apsd_result = smblib_get_apsd_result(chg);
- if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) {
- smblib_rerun_apsd(chg);
- }
- }
- }
- return 0;
-}
-
static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
{
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
@@ -685,6 +661,7 @@ static void smblib_uusb_removal(struct smb_charger *chg)
chg->voltage_max_uv = MICRO_5V;
chg->usb_icl_delta_ua = 0;
chg->pulse_cnt = 0;
+ chg->uusb_apsd_rerun_done = false;
/* clear USB ICL vote for USB_PSY_VOTER */
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
@@ -752,6 +729,7 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg)
rc);
}
+ chg->uusb_apsd_rerun_done = true;
smblib_rerun_apsd(chg);
return 0;
@@ -1021,6 +999,7 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
struct smb_charger *chg = data;
int rc;
u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT;
+ u8 stat;
/* vote to enable/disable HW autonomous INOV */
vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0);
@@ -1042,6 +1021,16 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
return rc;
}
+ rc = smblib_read(chg, APSD_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc);
+ return rc;
+ }
+
+ /* re-run APSD if HVDCP was detected */
+ if (stat & QC_CHARGER_BIT)
+ smblib_rerun_apsd(chg);
+
return 0;
}
@@ -1135,6 +1124,22 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
return 0;
}
+static int smblib_typec_irq_disable_vote_callback(struct votable *votable,
+ void *data, int disable, const char *client)
+{
+ struct smb_charger *chg = data;
+
+ if (!chg->irq_info[TYPE_C_CHANGE_IRQ].irq)
+ return 0;
+
+ if (disable)
+ disable_irq_nosync(chg->irq_info[TYPE_C_CHANGE_IRQ].irq);
+ else
+ enable_irq(chg->irq_info[TYPE_C_CHANGE_IRQ].irq);
+
+ return 0;
+}
+
/*******************
* VCONN REGULATOR *
* *****************/
@@ -1143,7 +1148,7 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev)
{
struct smb_charger *chg = rdev_get_drvdata(rdev);
- u8 otg_stat, stat4;
+ u8 otg_stat, val;
int rc = 0, i;
if (!chg->external_vconn) {
@@ -1174,17 +1179,12 @@ static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev)
* VCONN_EN_ORIENTATION is overloaded with overriding the CC pin used
* for Vconn, and it should be set with reverse polarity of CC_OUT.
*/
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return rc;
- }
-
smblib_dbg(chg, PR_OTG, "enabling VCONN\n");
- stat4 = stat4 & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
+ val = chg->typec_status[3] &
+ CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT,
- VCONN_EN_VALUE_BIT | stat4);
+ VCONN_EN_VALUE_BIT | val);
if (rc < 0) {
smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc);
return rc;
@@ -1532,6 +1532,21 @@ int smblib_get_prop_batt_status(struct smb_charger *chg,
break;
}
+ if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
+ return 0;
+
+ rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ stat &= ENABLE_TRICKLE_BIT | ENABLE_PRE_CHARGING_BIT |
+ ENABLE_FAST_CHARGING_BIT | ENABLE_FULLON_MODE_BIT;
+ if (!stat)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
return 0;
}
@@ -2132,23 +2147,13 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc = 0;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n",
- stat);
-
- if (stat & CC_ATTACHED_BIT)
- val->intval = (bool)(stat & CC_ORIENTATION_BIT) + 1;
+ if (chg->typec_status[3] & CC_ATTACHED_BIT)
+ val->intval =
+ (bool)(chg->typec_status[3] & CC_ORIENTATION_BIT) + 1;
else
val->intval = 0;
- return rc;
+ return 0;
}
static const char * const smblib_typec_mode_name[] = {
@@ -2166,17 +2171,7 @@ static const char * const smblib_typec_mode_name[] = {
static int smblib_get_prop_ufp_mode(struct smb_charger *chg)
{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_1_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc);
- return POWER_SUPPLY_TYPEC_NONE;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_1 = 0x%02x\n", stat);
-
- switch (stat) {
+ switch (chg->typec_status[0]) {
case 0:
return POWER_SUPPLY_TYPEC_NONE;
case UFP_TYPEC_RDSTD_BIT:
@@ -2194,17 +2189,7 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg)
static int smblib_get_prop_dfp_mode(struct smb_charger *chg)
{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_2_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_2 rc=%d\n", rc);
- return POWER_SUPPLY_TYPEC_NONE;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_2 = 0x%02x\n", stat);
-
- switch (stat & DFP_TYPEC_MASK) {
+ switch (chg->typec_status[1] & DFP_TYPEC_MASK) {
case DFP_RA_RA_BIT:
return POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER;
case DFP_RD_RD_BIT:
@@ -2225,28 +2210,17 @@ static int smblib_get_prop_dfp_mode(struct smb_charger *chg)
int smblib_get_prop_typec_mode(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+ if (!(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) {
val->intval = POWER_SUPPLY_TYPEC_NONE;
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat);
-
- if (!(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) {
- val->intval = POWER_SUPPLY_TYPEC_NONE;
- return rc;
+ return 0;
}
- if (stat & UFP_DFP_MODE_STATUS_BIT)
+ if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT)
val->intval = smblib_get_prop_dfp_mode(chg);
else
val->intval = smblib_get_prop_ufp_mode(chg);
- return rc;
+ return 0;
}
int smblib_get_prop_typec_power_role(struct smb_charger *chg,
@@ -2338,16 +2312,7 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc;
- u8 ctrl;
-
- rc = smblib_read(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, &ctrl);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG rc=%d\n",
- rc);
- return rc;
- }
- val->intval = ctrl & EXIT_SNK_BASED_ON_CC_BIT;
+ val->intval = chg->pd_hard_reset;
return 0;
}
@@ -2543,53 +2508,60 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
const union power_supply_propval *val)
{
int rc;
- u8 stat = 0;
- bool cc_debounced;
- bool orientation;
- bool pd_active = val->intval;
+ bool orientation, cc_debounced, sink_attached, hvdcp;
+ u8 stat;
- if (!get_effective_result(chg->pd_allowed_votable)) {
- smblib_err(chg, "PD is not allowed\n");
+ if (!get_effective_result(chg->pd_allowed_votable))
return -EINVAL;
- }
- vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0);
- vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0);
- vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0);
-
- /*
- * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line
- * when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) is set
- * or when VCONN_EN_VALUE_BIT is set.
- */
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ rc = smblib_read(chg, APSD_STATUS_REG, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+ smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc);
return rc;
}
- if (pd_active) {
- orientation = stat & CC_ORIENTATION_BIT;
+ cc_debounced = (bool)
+ (chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
+ sink_attached = (bool)
+ (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT);
+ hvdcp = stat & QC_CHARGER_BIT;
+
+ chg->pd_active = val->intval;
+ if (chg->pd_active) {
+ vote(chg->apsd_disable_votable, PD_VOTER, true, 0);
+ vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
+
+ /*
+ * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2
+ * line when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override)
+ * is set or when VCONN_EN_VALUE_BIT is set.
+ */
+ orientation = chg->typec_status[3] & CC_ORIENTATION_BIT;
rc = smblib_masked_write(chg,
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
VCONN_EN_ORIENTATION_BIT,
orientation ? 0 : VCONN_EN_ORIENTATION_BIT);
- if (rc < 0) {
+ if (rc < 0)
smblib_err(chg,
"Couldn't enable vconn on CC line rc=%d\n", rc);
- return rc;
- }
+
+ /* SW controlled CC_OUT */
+ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+ TYPEC_SPARE_CFG_BIT, TYPEC_SPARE_CFG_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable SW cc_out rc=%d\n",
+ rc);
+
/*
* Enforce 500mA for PD until the real vote comes in later.
* It is guaranteed that pd_active is set prior to
* pd_current_max
*/
rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
- if (rc < 0) {
+ if (rc < 0)
smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
- rc);
- return rc;
- }
+ rc);
/* since PD was found the cable must be non-legacy */
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
@@ -2597,36 +2569,40 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
/* clear USB ICL vote for DCP_VOTER */
rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote DCP from USB ICL rc=%d\n",
- rc);
+ smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n",
+ rc);
/* remove USB_PSY_VOTER */
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- if (rc < 0) {
+ if (rc < 0)
smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
- return rc;
- }
- }
+ } else {
+ vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
+ vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
+ false, 0);
- /* CC pin selection s/w override in PD session; h/w otherwise. */
- rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
- TYPEC_SPARE_CFG_BIT,
- pd_active ? TYPEC_SPARE_CFG_BIT : 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't change cc_out ctrl to %s rc=%d\n",
- pd_active ? "SW" : "HW", rc);
- return rc;
- }
+ /* HW controlled CC_OUT */
+ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+ TYPEC_SPARE_CFG_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n",
+ rc);
- cc_debounced = (bool)(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
- if (!pd_active && cc_debounced)
- try_rerun_apsd_for_hvdcp(chg);
+ /*
+ * This WA should only run for HVDCP. Non-legacy SDP/CDP could
+ * draw more, but this WA will remove Rd causing VBUS to drop,
+ * and data could be interrupted. Non-legacy DCP could also draw
+ * more, but it may impact compliance.
+ */
+ if (!chg->typec_legacy_valid && cc_debounced &&
+ !sink_attached && hvdcp)
+ schedule_work(&chg->legacy_detection_work);
+ }
- chg->pd_active = pd_active;
smblib_update_usb_type(chg);
power_supply_changed(chg->usb_psy);
-
return rc;
}
@@ -2729,88 +2705,70 @@ static struct reg_info cc2_detach_settings[] = {
static int smblib_cc2_sink_removal_enter(struct smb_charger *chg)
{
- int rc = 0;
- union power_supply_propval cc2_val = {0, };
+ int rc, ccout, ufp_mode;
+ u8 stat;
if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0)
- return rc;
+ return 0;
- if (chg->cc2_sink_detach_flag != CC2_SINK_NONE)
- return rc;
+ if (chg->cc2_detach_wa_active)
+ return 0;
- rc = smblib_get_prop_typec_cc_orientation(chg, &cc2_val);
+ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't get cc orientation rc=%d\n", rc);
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
return rc;
}
- if (cc2_val.intval == 1)
- return rc;
+ ccout = (stat & CC_ATTACHED_BIT) ?
+ (!!(stat & CC_ORIENTATION_BIT) + 1) : 0;
+ ufp_mode = (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT) ?
+ !(stat & UFP_DFP_MODE_STATUS_BIT) : 0;
- rc = smblib_get_prop_typec_mode(chg, &cc2_val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc);
- return rc;
- }
+ if (ccout != 2)
+ return 0;
- switch (cc2_val.intval) {
- case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
- smblib_reg_block_update(chg, cc2_detach_settings);
- chg->cc2_sink_detach_flag = CC2_SINK_STD;
- schedule_work(&chg->rdstd_cc2_detach_work);
- break;
- case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- chg->cc2_sink_detach_flag = CC2_SINK_MEDIUM_HIGH;
- break;
- default:
- break;
- }
+ if (!ufp_mode)
+ return 0;
+ chg->cc2_detach_wa_active = true;
+ /* The CC2 removal WA will cause a type-c-change IRQ storm */
+ smblib_reg_block_update(chg, cc2_detach_settings);
+ schedule_work(&chg->rdstd_cc2_detach_work);
return rc;
}
static int smblib_cc2_sink_removal_exit(struct smb_charger *chg)
{
- int rc = 0;
-
if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0)
- return rc;
-
- if (chg->cc2_sink_detach_flag == CC2_SINK_STD) {
- cancel_work_sync(&chg->rdstd_cc2_detach_work);
- smblib_reg_block_restore(chg, cc2_detach_settings);
- }
+ return 0;
- chg->cc2_sink_detach_flag = CC2_SINK_NONE;
+ if (!chg->cc2_detach_wa_active)
+ return 0;
- return rc;
+ chg->cc2_detach_wa_active = false;
+ cancel_work_sync(&chg->rdstd_cc2_detach_work);
+ smblib_reg_block_restore(chg, cc2_detach_settings);
+ return 0;
}
int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg,
const union power_supply_propval *val)
{
- int rc;
+ int rc = 0;
- rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
- EXIT_SNK_BASED_ON_CC_BIT,
- (val->intval) ? EXIT_SNK_BASED_ON_CC_BIT : 0);
- if (rc < 0) {
- smblib_err(chg, "Could not set EXIT_SNK_BASED_ON_CC rc=%d\n",
- rc);
+ if (chg->pd_hard_reset == val->intval)
return rc;
- }
- vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, val->intval, 0);
+ chg->pd_hard_reset = val->intval;
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ EXIT_SNK_BASED_ON_CC_BIT,
+ (chg->pd_hard_reset) ? EXIT_SNK_BASED_ON_CC_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't set EXIT_SNK_BASED_ON_CC rc=%d\n",
+ rc);
- if (val->intval)
- rc = smblib_cc2_sink_removal_enter(chg);
- else
- rc = smblib_cc2_sink_removal_exit(chg);
-
- if (rc < 0) {
- smblib_err(chg, "Could not detect cc2 removal rc=%d\n", rc);
- return rc;
- }
+ vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER,
+ chg->pd_hard_reset, 0);
return rc;
}
@@ -3117,25 +3075,43 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data)
return IRQ_HANDLED;
}
+static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
+{
+ if (vbus_rising) {
+ /* use the typec flag even though its not typec */
+ chg->typec_present = 1;
+ } else {
+ chg->typec_present = 0;
+ smblib_update_usb_type(chg);
+ extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
+ smblib_uusb_removal(chg);
+ }
+}
+
+static void smblib_typec_usb_plugin(struct smb_charger *chg, bool vbus_rising)
+{
+ if (vbus_rising)
+ smblib_cc2_sink_removal_exit(chg);
+ else
+ smblib_cc2_sink_removal_enter(chg);
+}
+
#define PL_DELAY_MS 30000
-irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
+void smblib_usb_plugin_locked(struct smb_charger *chg)
{
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
int rc;
u8 stat;
bool vbus_rising;
rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
if (rc < 0) {
- dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
- return IRQ_HANDLED;
+ smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
+ return;
}
vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
- smblib_set_opt_freq_buck(chg,
- vbus_rising ? chg->chg_freq.freq_5V :
- chg->chg_freq.freq_removal);
+ smblib_set_opt_freq_buck(chg, vbus_rising ? chg->chg_freq.freq_5V :
+ chg->chg_freq.freq_removal);
/* fetch the DPDM regulator */
if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
@@ -3172,17 +3148,26 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
rc);
}
-
- if (chg->micro_usb_mode) {
- smblib_update_usb_type(chg);
- extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
- smblib_uusb_removal(chg);
- }
}
+ if (chg->micro_usb_mode)
+ smblib_micro_usb_plugin(chg, vbus_rising);
+ else
+ smblib_typec_usb_plugin(chg, vbus_rising);
+
power_supply_changed(chg->usb_psy);
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n",
- irq_data->name, vbus_rising ? "attached" : "detached");
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
+ vbus_rising ? "attached" : "detached");
+}
+
+irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ mutex_lock(&chg->lock);
+ smblib_usb_plugin_locked(chg);
+ mutex_unlock(&chg->lock);
return IRQ_HANDLED;
}
@@ -3351,9 +3336,6 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
if (rising) {
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
- if (get_effective_result(chg->pd_disallowed_votable_indirect))
- /* could be a legacy cable, try doing hvdcp */
- try_rerun_apsd_for_hvdcp(chg);
/* enable HDC and ICL irq for QC2/3 charger */
if (qc_charger)
@@ -3388,6 +3370,10 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
{
+ /* while PD is active it should have complete ICL control */
+ if (chg->pd_active)
+ return;
+
switch (pst) {
case POWER_SUPPLY_TYPE_USB:
/*
@@ -3427,7 +3413,7 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
apsd_result = smblib_update_usb_type(chg);
- if (!chg->pd_active)
+ if (!chg->typec_legacy_valid)
smblib_force_legacy_icl(chg, apsd_result->pst);
switch (apsd_result->bit) {
@@ -3469,6 +3455,17 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data)
}
smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat);
+ if (chg->micro_usb_mode && (stat & APSD_DTC_STATUS_DONE_BIT)
+ && !chg->uusb_apsd_rerun_done) {
+ /*
+ * Force re-run APSD to handle slow insertion related
+ * charger-mis-detection.
+ */
+ chg->uusb_apsd_rerun_done = true;
+ smblib_rerun_apsd(chg);
+ return IRQ_HANDLED;
+ }
+
smblib_handle_apsd_done(chg,
(bool)(stat & APSD_DTC_STATUS_DONE_BIT));
@@ -3502,71 +3499,6 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data)
return IRQ_HANDLED;
}
-static void typec_source_removal(struct smb_charger *chg)
-{
- int rc;
-
- /* reset legacy unknown vote */
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
-
- /* reset both usbin current and voltage votes */
- vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
- vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
-
- cancel_delayed_work_sync(&chg->hvdcp_detect_work);
-
- if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) {
- /* re-enable AUTH_IRQ_EN_CFG_BIT */
- rc = smblib_masked_write(chg,
- USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
- AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't enable QC auth setting rc=%d\n", rc);
- }
-
- /* reconfigure allowed voltage for HVDCP */
- rc = smblib_set_adapter_allowance(chg,
- USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
- if (rc < 0)
- smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
- rc);
-
- chg->voltage_min_uv = MICRO_5V;
- chg->voltage_max_uv = MICRO_5V;
-
- /* clear USB ICL vote for PD_VOTER */
- rc = vote(chg->usb_icl_votable, PD_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't un-vote PD from USB ICL rc=%d\n", rc);
-
- /* clear USB ICL vote for USB_PSY_VOTER */
- rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote USB_PSY from USB ICL rc=%d\n", rc);
-
- /* clear USB ICL vote for DCP_VOTER */
- rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote DCP from USB ICL rc=%d\n", rc);
-
-}
-
-static void typec_source_insertion(struct smb_charger *chg)
-{
- /*
- * at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for
- * ICL, so vote LEGACY_UNKNOWN here if none of the above three have
- * casted their votes
- */
- if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER)
- && !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER)
- && !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER))
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
-}
-
static void typec_sink_insertion(struct smb_charger *chg)
{
/* when a sink is inserted we should not wait on hvdcp timeout to
@@ -3587,30 +3519,50 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
{
int rc;
+ chg->cc2_detach_wa_active = false;
+
+ /* reset APSD voters */
+ vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
+ vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
+
cancel_delayed_work_sync(&chg->pl_enable_work);
- vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
- vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
+ cancel_delayed_work_sync(&chg->hvdcp_detect_work);
+
+ /* reset input current limit voters */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
+ vote(chg->usb_icl_votable, PD_VOTER, false, 0);
+ vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
+ vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
+ vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
+ /* reset hvdcp voters */
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0);
+
+ /* reset power delivery voters */
+ vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0);
+
+ /* reset usb irq voters */
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0);
- /* reset votes from vbus_cc_short */
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- true, 0);
- vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
- true, 0);
- /*
- * cable could be removed during hard reset, remove its vote to
- * disable apsd
- */
- vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
+ /* reset parallel voters */
+ vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
+ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
+ vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
+ vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
chg->vconn_attempts = 0;
chg->otg_attempts = 0;
chg->pulse_cnt = 0;
chg->usb_icl_delta_ua = 0;
+ chg->voltage_min_uv = MICRO_5V;
+ chg->voltage_max_uv = MICRO_5V;
+ chg->pd_active = 0;
+ chg->pd_hard_reset = 0;
+ chg->typec_legacy_valid = false;
/* enable APSD CC trigger for next insertion */
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
@@ -3618,15 +3570,48 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
if (rc < 0)
smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc);
- smblib_update_usb_type(chg);
- typec_source_removal(chg);
+ if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) {
+ /* re-enable AUTH_IRQ_EN_CFG_BIT */
+ rc = smblib_masked_write(chg,
+ USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
+ AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't enable QC auth setting rc=%d\n", rc);
+ }
+
+ /* reconfigure allowed voltage for HVDCP */
+ rc = smblib_set_adapter_allowance(chg,
+ USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
+ rc);
+
+ /* enable DRP */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
+
+ /* HW controlled CC_OUT */
+ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+ TYPEC_SPARE_CFG_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
+
+ /* restore crude sensor */
+ rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't restore crude sensor rc=%d\n", rc);
+
typec_sink_removal(chg);
+ smblib_update_usb_type(chg);
}
static void smblib_handle_typec_insertion(struct smb_charger *chg,
- bool sink_attached, bool legacy_cable)
+ bool sink_attached)
{
- int rp, rc;
+ int rc;
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
@@ -3636,59 +3621,36 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg,
smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
rc);
- if (sink_attached) {
- typec_source_removal(chg);
+ if (sink_attached)
typec_sink_insertion(chg);
- } else {
- typec_source_insertion(chg);
+ else
typec_sink_removal(chg);
- }
-
- rp = smblib_get_prop_ufp_mode(chg);
- if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH
- || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) {
- smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n");
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- true, 0);
- } else {
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- false, 0);
- }
}
static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
- bool rising, bool sink_attached, bool legacy_cable)
+ bool rising, bool sink_attached)
{
int rc;
union power_supply_propval pval = {0, };
- if (rising)
- smblib_handle_typec_insertion(chg, sink_attached, legacy_cable);
- else
- smblib_handle_typec_removal(chg);
+ if (rising) {
+ if (!chg->typec_present) {
+ chg->typec_present = true;
+ smblib_dbg(chg, PR_MISC, "TypeC insertion\n");
+ smblib_handle_typec_insertion(chg, sink_attached);
+ }
+ } else {
+ if (chg->typec_present) {
+ chg->typec_present = false;
+ smblib_dbg(chg, PR_MISC, "TypeC removal\n");
+ smblib_handle_typec_removal(chg);
+ }
+ }
rc = smblib_get_prop_typec_mode(chg, &pval);
if (rc < 0)
smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc);
- /*
- * HW BUG - after cable is removed, medium or high rd reading
- * falls to std. Use it for signal of typec cc detachment in
- * software WA.
- */
- if (chg->cc2_sink_detach_flag == CC2_SINK_MEDIUM_HIGH
- && pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
-
- chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE;
-
- rc = smblib_masked_write(chg,
- TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
- EXIT_SNK_BASED_ON_CC_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't get prop typec mode rc=%d\n",
- rc);
- }
-
smblib_dbg(chg, PR_INTERRUPT, "IRQ: debounce-done %s; Type-C %s detected\n",
rising ? "rising" : "falling",
smblib_typec_mode_name[pval.intval]);
@@ -3714,50 +3676,54 @@ irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg)
return IRQ_HANDLED;
}
-irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
+static void smblib_usb_typec_change(struct smb_charger *chg)
{
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
int rc;
- u8 stat4, stat5;
- bool debounce_done, sink_attached, legacy_cable;
-
- if (chg->micro_usb_mode)
- return smblib_handle_usb_typec_change_for_uusb(chg);
-
- /* WA - not when PD hard_reset WIP on cc2 in sink mode */
- if (chg->cc2_sink_detach_flag == CC2_SINK_STD)
- return IRQ_HANDLED;
-
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return IRQ_HANDLED;
- }
+ bool debounce_done, sink_attached;
- rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5);
+ rc = smblib_multibyte_read(chg, TYPE_C_STATUS_1_REG,
+ chg->typec_status, 5);
if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc);
- return IRQ_HANDLED;
+ smblib_err(chg, "Couldn't cache USB Type-C status rc=%d\n", rc);
+ return;
}
- debounce_done = (bool)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
- sink_attached = (bool)(stat4 & UFP_DFP_MODE_STATUS_BIT);
- legacy_cable = (bool)(stat5 & TYPEC_LEGACY_CABLE_STATUS_BIT);
+ debounce_done =
+ (bool)(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
+ sink_attached =
+ (bool)(chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT);
- smblib_handle_typec_debounce_done(chg,
- debounce_done, sink_attached, legacy_cable);
+ smblib_handle_typec_debounce_done(chg, debounce_done, sink_attached);
- if (stat4 & TYPEC_VBUS_ERROR_STATUS_BIT)
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s vbus-error\n",
- irq_data->name);
+ if (chg->typec_status[3] & TYPEC_VBUS_ERROR_STATUS_BIT)
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n");
- if (stat4 & TYPEC_VCONN_OVERCURR_STATUS_BIT)
+ if (chg->typec_status[3] & TYPEC_VCONN_OVERCURR_STATUS_BIT)
schedule_work(&chg->vconn_oc_work);
power_supply_changed(chg->usb_psy);
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat4);
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_5 = 0x%02x\n", stat5);
+}
+
+irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ if (chg->micro_usb_mode) {
+ smblib_handle_usb_typec_change_for_uusb(chg);
+ return IRQ_HANDLED;
+ }
+
+ if (chg->cc2_detach_wa_active || chg->typec_en_dis_active) {
+ smblib_dbg(chg, PR_INTERRUPT, "Ignoring since %s active\n",
+ chg->cc2_detach_wa_active ?
+ "cc2_detach_wa" : "typec_en_dis");
+ return IRQ_HANDLED;
+ }
+
+ mutex_lock(&chg->lock);
+ smblib_usb_typec_change(chg);
+ mutex_unlock(&chg->lock);
return IRQ_HANDLED;
}
@@ -3785,7 +3751,7 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
- int rc;
+ int rc, usb_icl;
u8 stat;
if (!(chg->wa_flags & BOOST_BACK_WA))
@@ -3797,8 +3763,9 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
return IRQ_HANDLED;
}
- if ((stat & USE_USBIN_BIT) &&
- get_effective_result(chg->usb_icl_votable) < USBIN_25MA)
+ /* skip suspending input if its already suspended by some other voter */
+ usb_icl = get_effective_result(chg->usb_icl_votable);
+ if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl < USBIN_25MA)
return IRQ_HANDLED;
if (stat & USE_DCIN_BIT)
@@ -3836,12 +3803,7 @@ static void smblib_hvdcp_detect_work(struct work_struct *work)
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
- if (get_effective_result(chg->pd_disallowed_votable_indirect))
- /* pd is still disabled, try hvdcp */
- try_rerun_apsd_for_hvdcp(chg);
- else
- /* notify pd now that pd is allowed */
- power_supply_changed(chg->usb_psy);
+ power_supply_changed(chg->usb_psy);
}
static void bms_update_work(struct work_struct *work)
@@ -3882,11 +3844,13 @@ static void clear_hdc_work(struct work_struct *work)
static void rdstd_cc2_detach_work(struct work_struct *work)
{
int rc;
- u8 stat;
- struct smb_irq_data irq_data = {NULL, "cc2-removal-workaround"};
+ u8 stat4, stat5;
struct smb_charger *chg = container_of(work, struct smb_charger,
rdstd_cc2_detach_work);
+ if (!chg->cc2_detach_wa_active)
+ return;
+
/*
* WA steps -
* 1. Enable both UFP and DFP, wait for 10ms.
@@ -3894,7 +3858,7 @@ static void rdstd_cc2_detach_work(struct work_struct *work)
* 3. Removal detected if both TYPEC_DEBOUNCE_DONE_STATUS
* and TIMER_STAGE bits are gone, otherwise repeat all by
* work rescheduling.
- * Note, work will be cancelled when pd_hard_reset is 0.
+ * Note, work will be cancelled when USB_PLUGIN rises.
*/
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
@@ -3917,30 +3881,35 @@ static void rdstd_cc2_detach_work(struct work_struct *work)
usleep_range(30000, 31000);
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4);
if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
- rc);
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
return;
}
- if (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)
- goto rerun;
- rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
+ rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5);
if (rc < 0) {
smblib_err(chg,
"Couldn't read TYPE_C_STATUS_5_REG rc=%d\n", rc);
return;
}
- if (stat & TIMER_STAGE_2_BIT)
+
+ if ((stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT)
+ || (stat5 & TIMER_STAGE_2_BIT)) {
+ smblib_dbg(chg, PR_MISC, "rerunning DD=%d TS2BIT=%d\n",
+ (int)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT),
+ (int)(stat5 & TIMER_STAGE_2_BIT));
goto rerun;
+ }
- /* Bingo, cc2 removal detected */
+ smblib_dbg(chg, PR_MISC, "Bingo CC2 Removal detected\n");
+ chg->cc2_detach_wa_active = false;
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ EXIT_SNK_BASED_ON_CC_BIT, 0);
smblib_reg_block_restore(chg, cc2_detach_settings);
- chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE;
- irq_data.parent_data = chg;
- smblib_handle_usb_typec_change(0, &irq_data);
-
+ mutex_lock(&chg->lock);
+ smblib_usb_typec_change(chg);
+ mutex_unlock(&chg->lock);
return;
rerun:
@@ -4163,6 +4132,56 @@ static void smblib_pl_enable_work(struct work_struct *work)
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
}
+static void smblib_legacy_detection_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ legacy_detection_work);
+ int rc;
+ u8 stat;
+ bool legacy, rp_high;
+
+ mutex_lock(&chg->lock);
+ chg->typec_en_dis_active = 1;
+ smblib_dbg(chg, PR_MISC, "running legacy unknown workaround\n");
+ rc = smblib_masked_write(chg,
+ TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT,
+ TYPEC_DISABLE_CMD_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc);
+
+ /* wait for the adapter to turn off VBUS */
+ msleep(500);
+
+ rc = smblib_masked_write(chg,
+ TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc);
+
+ /* wait for type-c detection to complete */
+ msleep(100);
+
+ rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read typec stat5 rc = %d\n", rc);
+ goto unlock;
+ }
+
+ chg->typec_legacy_valid = true;
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+ legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
+ rp_high = smblib_get_prop_ufp_mode(chg) ==
+ POWER_SUPPLY_TYPEC_SOURCE_HIGH;
+ if (!legacy || !rp_high)
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ false, 0);
+
+unlock:
+ chg->typec_en_dis_active = 0;
+ mutex_unlock(&chg->lock);
+}
+
static int smblib_create_votables(struct smb_charger *chg)
{
int rc = 0;
@@ -4295,6 +4314,15 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+ chg->typec_irq_disable_votable = create_votable("TYPEC_IRQ_DISABLE",
+ VOTE_SET_ANY,
+ smblib_typec_irq_disable_vote_callback,
+ chg);
+ if (IS_ERR(chg->typec_irq_disable_votable)) {
+ rc = PTR_ERR(chg->typec_irq_disable_votable);
+ return rc;
+ }
+
return rc;
}
@@ -4320,6 +4348,8 @@ static void smblib_destroy_votables(struct smb_charger *chg)
destroy_votable(chg->apsd_disable_votable);
if (chg->hvdcp_hw_inov_dis_votable)
destroy_votable(chg->hvdcp_hw_inov_dis_votable);
+ if (chg->typec_irq_disable_votable)
+ destroy_votable(chg->typec_irq_disable_votable);
}
static void smblib_iio_deinit(struct smb_charger *chg)
@@ -4340,6 +4370,7 @@ int smblib_init(struct smb_charger *chg)
{
int rc = 0;
+ mutex_init(&chg->lock);
mutex_init(&chg->write_lock);
mutex_init(&chg->otg_oc_lock);
INIT_WORK(&chg->bms_update_work, bms_update_work);
@@ -4352,6 +4383,7 @@ int smblib_init(struct smb_charger *chg)
INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work);
INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
+ INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
chg->fake_capacity = -EINVAL;
switch (chg->mode) {
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 49b9d3da783c..b0d84f014b0d 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -61,6 +61,7 @@ enum print_reason {
#define SW_QC3_VOTER "SW_QC3_VOTER"
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER"
+#define CC2_WA_VOTER "CC2_WA_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -71,13 +72,6 @@ enum smb_mode {
NUM_MODES,
};
-enum cc2_sink_type {
- CC2_SINK_NONE = 0,
- CC2_SINK_STD,
- CC2_SINK_MEDIUM_HIGH,
- CC2_SINK_WA_DONE,
-};
-
enum {
QC_CHARGER_DETECTION_WA_BIT = BIT(0),
BOOST_BACK_WA = BIT(1),
@@ -236,6 +230,7 @@ struct smb_charger {
int smb_version;
/* locks */
+ struct mutex lock;
struct mutex write_lock;
struct mutex ps_change_lock;
struct mutex otg_oc_lock;
@@ -276,6 +271,7 @@ struct smb_charger {
struct votable *apsd_disable_votable;
struct votable *hvdcp_hw_inov_dis_votable;
struct votable *usb_irq_enable_votable;
+ struct votable *typec_irq_disable_votable;
/* work */
struct work_struct bms_update_work;
@@ -289,6 +285,7 @@ struct smb_charger {
struct delayed_work otg_ss_done_work;
struct delayed_work icl_change_work;
struct delayed_work pl_enable_work;
+ struct work_struct legacy_detection_work;
/* cached status */
int voltage_min_uv;
@@ -312,10 +309,16 @@ struct smb_charger {
int vconn_attempts;
int default_icl_ua;
int otg_cl_ua;
+ bool uusb_apsd_rerun_done;
+ bool pd_hard_reset;
+ bool typec_present;
+ u8 typec_status[5];
+ bool typec_legacy_valid;
/* workaround flag */
u32 wa_flags;
- enum cc2_sink_type cc2_sink_detach_flag;
+ bool cc2_detach_wa_active;
+ bool typec_en_dis_active;
int boost_current_ua;
int temp_speed_reading_count;
diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h
index 167666a8c548..3f260a407721 100644
--- a/drivers/power/supply/qcom/smb-reg.h
+++ b/drivers/power/supply/qcom/smb-reg.h
@@ -1025,4 +1025,14 @@ enum {
/* CHGR FREQ Peripheral registers */
#define FREQ_CLK_DIV_REG (CHGR_FREQ_BASE + 0x50)
+/* SMB1355 specific registers */
+#define SMB1355_TEMP_COMP_STATUS_REG (MISC_BASE + 0x07)
+#define SKIN_TEMP_RST_HOT_BIT BIT(6)
+#define SKIN_TEMP_UB_HOT_BIT BIT(5)
+#define SKIN_TEMP_LB_HOT_BIT BIT(4)
+#define DIE_TEMP_TSD_HOT_BIT BIT(3)
+#define DIE_TEMP_RST_HOT_BIT BIT(2)
+#define DIE_TEMP_UB_HOT_BIT BIT(1)
+#define DIE_TEMP_LB_HOT_BIT BIT(0)
+
#endif /* __SMB2_CHARGER_REG_H */
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 694591c3ec56..4e710cae6b78 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -104,6 +104,8 @@ struct smb138x {
struct smb_dt_props dt;
struct power_supply *parallel_psy;
u32 wa_flags;
+ struct pmic_revid_data *pmic_rev_id;
+ char *name;
};
static int __debug_mask;
@@ -167,6 +169,14 @@ static int smb138x_parse_dt(struct smb138x *chip)
if (rc < 0)
chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID;
+ /* check that smb1355 is configured to run in mid-mid mode */
+ if (chip->pmic_rev_id->pmic_subtype == SMB1355_SUBTYPE
+ && chip->dt.pl_mode != POWER_SUPPLY_PL_USBMID_USBMID) {
+ pr_err("Smb1355 can only run in MID-MID mode, saw = %d mode\n",
+ chip->dt.pl_mode);
+ return -EINVAL;
+ }
+
chip->dt.suspend_input = of_property_read_bool(node,
"qcom,suspend-input");
@@ -479,6 +489,30 @@ static int smb138x_init_batt_psy(struct smb138x *chip)
* PARALLEL PSY REGISTRATION *
*****************************/
+static int smb1355_get_prop_connector_health(struct smb138x *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+ u8 temp;
+ int rc;
+
+ rc = smblib_read(chg, SMB1355_TEMP_COMP_STATUS_REG, &temp);
+ if (rc < 0) {
+ pr_err("Couldn't read comp stat reg rc = %d\n", rc);
+ return POWER_SUPPLY_HEALTH_UNKNOWN;
+ }
+
+ if (temp & SKIN_TEMP_RST_HOT_BIT)
+ return POWER_SUPPLY_HEALTH_OVERHEAT;
+
+ if (temp & SKIN_TEMP_UB_HOT_BIT)
+ return POWER_SUPPLY_HEALTH_HOT;
+
+ if (temp & SKIN_TEMP_LB_HOT_BIT)
+ return POWER_SUPPLY_HEALTH_WARM;
+
+ return POWER_SUPPLY_HEALTH_COOL;
+}
+
static int smb138x_get_prop_connector_health(struct smb138x *chip)
{
struct smb_charger *chg = &chip->chg;
@@ -536,16 +570,32 @@ static enum power_supply_property smb138x_parallel_props[] = {
POWER_SUPPLY_PROP_PIN_ENABLED,
POWER_SUPPLY_PROP_INPUT_SUSPEND,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
- POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
- POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_PARALLEL_MODE,
+ POWER_SUPPLY_PROP_CONNECTOR_HEALTH,
+ POWER_SUPPLY_PROP_SET_SHIP_MODE,
POWER_SUPPLY_PROP_CHARGER_TEMP,
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static enum power_supply_property smb1355_parallel_props[] = {
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_PIN_ENABLED,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_PARALLEL_MODE,
POWER_SUPPLY_PROP_CONNECTOR_HEALTH,
POWER_SUPPLY_PROP_SET_SHIP_MODE,
+ POWER_SUPPLY_PROP_CHARGER_TEMP,
+ POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
};
static int smb138x_parallel_get_prop(struct power_supply *psy,
@@ -583,14 +633,6 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
else
val->intval = 0;
break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
- || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
- rc = smblib_get_charge_param(chg, &chg->param.usb_icl,
- &val->intval);
- else
- val->intval = 0;
- break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval);
break;
@@ -598,28 +640,46 @@ 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_CURRENT_NOW:
- rc = smblib_get_prop_slave_current_now(chg, val);
- break;
- case POWER_SUPPLY_PROP_CHARGER_TEMP:
- rc = smb138x_get_prop_charger_temp(chip, val);
- break;
- case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
- rc = smblib_get_prop_charger_temp_max(chg, val);
- break;
case POWER_SUPPLY_PROP_MODEL_NAME:
- val->strval = "smb138x";
+ val->strval = chip->name;
break;
case POWER_SUPPLY_PROP_PARALLEL_MODE:
val->intval = chip->dt.pl_mode;
break;
case POWER_SUPPLY_PROP_CONNECTOR_HEALTH:
- val->intval = smb138x_get_prop_connector_health(chip);
+ if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE)
+ val->intval = smb138x_get_prop_connector_health(chip);
+ else
+ val->intval = smb1355_get_prop_connector_health(chip);
break;
case POWER_SUPPLY_PROP_SET_SHIP_MODE:
/* Not in ship mode as long as device is active */
val->intval = 0;
break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP:
+ if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE)
+ rc = smb138x_get_prop_charger_temp(chip, val);
+ else
+ 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_CURRENT_NOW:
+ if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE)
+ rc = smblib_get_prop_slave_current_now(chg, val);
+ else
+ rc = -ENODATA;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ if ((chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE)
+ && ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)))
+ rc = smblib_get_charge_param(chg, &chg->param.usb_icl,
+ &val->intval);
+ else
+ rc = -ENODATA;
+ break;
default:
pr_err("parallel power supply get prop %d not supported\n",
prop);
@@ -703,7 +763,7 @@ static int smb138x_parallel_prop_is_writeable(struct power_supply *psy,
return 0;
}
-static const struct power_supply_desc parallel_psy_desc = {
+static struct power_supply_desc parallel_psy_desc = {
.name = "parallel",
.type = POWER_SUPPLY_TYPE_PARALLEL,
.properties = smb138x_parallel_props,
@@ -731,6 +791,28 @@ static int smb138x_init_parallel_psy(struct smb138x *chip)
return 0;
}
+static int smb1355_init_parallel_psy(struct smb138x *chip)
+{
+ struct power_supply_config parallel_cfg = {};
+ struct smb_charger *chg = &chip->chg;
+
+ parallel_cfg.drv_data = chip;
+ parallel_cfg.of_node = chg->dev->of_node;
+
+ /* change to smb1355's property list */
+ parallel_psy_desc.properties = smb1355_parallel_props;
+ parallel_psy_desc.num_properties = ARRAY_SIZE(smb1355_parallel_props);
+ chip->parallel_psy = devm_power_supply_register(chg->dev,
+ &parallel_psy_desc,
+ &parallel_cfg);
+ if (IS_ERR(chip->parallel_psy)) {
+ pr_err("Couldn't register parallel power supply\n");
+ return PTR_ERR(chip->parallel_psy);
+ }
+
+ return 0;
+}
+
/******************************
* VBUS REGULATOR REGISTRATION *
******************************/
@@ -1050,7 +1132,6 @@ static int smb138x_init_hw(struct smb138x *chip)
static int smb138x_setup_wa_flags(struct smb138x *chip)
{
- struct pmic_revid_data *pmic_rev_id;
struct device_node *revid_dev_node;
revid_dev_node = of_parse_phandle(chip->chg.dev->of_node,
@@ -1060,8 +1141,8 @@ static int smb138x_setup_wa_flags(struct smb138x *chip)
return -EINVAL;
}
- pmic_rev_id = get_revid_data(revid_dev_node);
- if (IS_ERR_OR_NULL(pmic_rev_id)) {
+ chip->pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR_OR_NULL(chip->pmic_rev_id)) {
/*
* the revid peripheral must be registered, any failure
* here only indicates that the rev-id module has not
@@ -1070,14 +1151,14 @@ static int smb138x_setup_wa_flags(struct smb138x *chip)
return -EPROBE_DEFER;
}
- switch (pmic_rev_id->pmic_subtype) {
+ switch (chip->pmic_rev_id->pmic_subtype) {
case SMB1381_SUBTYPE:
- if (pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */
+ if (chip->pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */
chip->wa_flags |= OOB_COMP_WA_BIT;
break;
default:
pr_err("PMIC subtype %d not supported\n",
- pmic_rev_id->pmic_subtype);
+ chip->pmic_rev_id->pmic_subtype);
return -EINVAL;
}
@@ -1375,6 +1456,7 @@ static int smb138x_master_probe(struct smb138x *chip)
chg->param = v1_params;
+ chip->name = "smb1381";
rc = smblib_init(chg);
if (rc < 0) {
pr_err("Couldn't initialize smblib rc=%d\n", rc);
@@ -1435,7 +1517,7 @@ static int smb138x_master_probe(struct smb138x *chip)
return rc;
}
-static int smb138x_slave_probe(struct smb138x *chip)
+static int smb1355_slave_probe(struct smb138x *chip)
{
struct smb_charger *chg = &chip->chg;
int rc = 0;
@@ -1448,6 +1530,55 @@ static int smb138x_slave_probe(struct smb138x *chip)
goto cleanup;
}
+ rc = smb138x_parse_dt(chip);
+ if (rc < 0) {
+ pr_err("Couldn't parse device tree rc=%d\n", rc);
+ goto cleanup;
+ }
+
+ rc = smb138x_init_slave_hw(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize hardware rc=%d\n", rc);
+ goto cleanup;
+ }
+
+ rc = smb1355_init_parallel_psy(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize parallel psy rc=%d\n", rc);
+ goto cleanup;
+ }
+
+ rc = smb138x_determine_initial_slave_status(chip);
+ if (rc < 0) {
+ pr_err("Couldn't determine initial status rc=%d\n", rc);
+ goto cleanup;
+ }
+
+ rc = smb138x_request_interrupts(chip);
+ if (rc < 0) {
+ pr_err("Couldn't request interrupts rc=%d\n", rc);
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ smblib_deinit(chg);
+ return rc;
+}
+
+static int smb1381_slave_probe(struct smb138x *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+ int rc = 0;
+
+ chg->param = v1_params;
+
+ rc = smblib_init(chg);
+ if (rc < 0) {
+ pr_err("Couldn't initialize smblib rc=%d\n", rc);
+ goto cleanup;
+ }
chg->iio.temp_max_chan = iio_channel_get(chg->dev, "charger_temp_max");
if (IS_ERR(chg->iio.temp_max_chan)) {
rc = PTR_ERR(chg->iio.temp_max_chan);
@@ -1515,25 +1646,71 @@ static int smb138x_slave_probe(struct smb138x *chip)
goto cleanup;
}
- return rc;
+ return 0;
cleanup:
smblib_deinit(chg);
- if (chip->parallel_psy)
- power_supply_unregister(chip->parallel_psy);
- if (chg->vbus_vreg && chg->vbus_vreg->rdev)
- regulator_unregister(chg->vbus_vreg->rdev);
return rc;
}
+static int slave_probe(struct smb138x *chip)
+{
+ struct device_node *revid_dev_node;
+ int rc = 0;
+
+ revid_dev_node = of_parse_phandle(chip->chg.dev->of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property\n");
+ return -EINVAL;
+ }
+
+ chip->pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR_OR_NULL(chip->pmic_rev_id)) {
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ switch (chip->pmic_rev_id->pmic_subtype) {
+ case SMB1355_SUBTYPE:
+ chip->name = "smb1355";
+ rc = smb1355_slave_probe(chip);
+ break;
+ case SMB1381_SUBTYPE:
+ chip->name = "smb1381";
+ rc = smb1381_slave_probe(chip);
+ break;
+ default:
+ pr_err("Unsupported pmic subtype = 0x%02x\n",
+ chip->pmic_rev_id->pmic_subtype);
+ rc = -EINVAL;
+ }
+
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ pr_err("Couldn't probe SMB138X rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
static const struct of_device_id match_table[] = {
{
- .compatible = "qcom,smb138x-charger",
- .data = (void *) PARALLEL_MASTER
+ .compatible = "qcom,smb138x-charger",
+ .data = (void *) PARALLEL_MASTER,
+ },
+ {
+ .compatible = "qcom,smb138x-parallel-slave",
+ .data = (void *) PARALLEL_SLAVE,
},
{
- .compatible = "qcom,smb138x-parallel-slave",
- .data = (void *) PARALLEL_SLAVE
+ .compatible = "qcom,smb1355-parallel-slave",
+ .data = (void *) PARALLEL_SLAVE,
},
{ },
};
@@ -1580,7 +1757,7 @@ static int smb138x_probe(struct platform_device *pdev)
rc = smb138x_master_probe(chip);
break;
case PARALLEL_SLAVE:
- rc = smb138x_slave_probe(chip);
+ rc = slave_probe(chip);
break;
default:
pr_err("Couldn't find a matching mode %d\n", chip->chg.mode);
@@ -1594,7 +1771,8 @@ static int smb138x_probe(struct platform_device *pdev)
goto cleanup;
}
- pr_info("SMB138X probed successfully mode=%d\n", chip->chg.mode);
+ pr_info("%s probed successfully mode=%d pl_mode = %d\n",
+ chip->name, chip->chg.mode, chip->dt.pl_mode);
return rc;
cleanup:
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index dbe2a08f1776..858ddcc228df 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -559,6 +559,7 @@ struct lab_regulator {
int step_size;
int slew_rate;
int soft_start;
+ int sc_wait_time_ms;
int vreg_enabled;
};
@@ -608,6 +609,8 @@ struct qpnp_labibb {
bool skip_2nd_swire_cmd;
bool pfm_enable;
bool notify_lab_vreg_ok_sts;
+ bool detect_lab_sc;
+ bool sc_detected;
u32 swire_2nd_cmd_delay;
u32 swire_ibb_ps_enable_delay;
};
@@ -2138,8 +2141,10 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
u8 val;
struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb,
lab_vreg_ok_work);
+ if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL)
+ retries = labibb->lab_vreg.sc_wait_time_ms / 5;
- while (retries--) {
+ while (retries) {
rc = qpnp_labibb_read(labibb, labibb->lab_base +
REG_LAB_STATUS1, &val, 1);
if (rc < 0) {
@@ -2155,10 +2160,30 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
}
usleep_range(dly, dly + 100);
+ retries--;
}
- if (!retries)
- pr_err("LAB_VREG_OK not set, failed to notify\n");
+ if (!retries) {
+ if (labibb->detect_lab_sc) {
+ pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n");
+ /* Disable LAB module */
+ val = 0;
+ rc = qpnp_labibb_write(labibb, labibb->lab_base +
+ REG_LAB_MODULE_RDY, &val, 1);
+ if (rc < 0) {
+ pr_err("write register %x failed rc = %d\n",
+ REG_LAB_MODULE_RDY, rc);
+ return;
+ }
+ raw_notifier_call_chain(&labibb_notifier,
+ LAB_VREG_NOT_OK, NULL);
+ labibb->sc_detected = true;
+ labibb->lab_vreg.vreg_enabled = 0;
+ labibb->ibb_vreg.vreg_enabled = 0;
+ } else {
+ pr_err("LAB_VREG_OK not set, failed to notify\n");
+ }
+ }
}
static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
@@ -2323,6 +2348,11 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
+ if (labibb->sc_detected) {
+ pr_info("Short circuit detected: disabled LAB/IBB rails\n");
+ return 0;
+ }
+
if (labibb->skip_2nd_swire_cmd) {
rc = qpnp_ibb_ps_config(labibb, false);
if (rc < 0) {
@@ -2363,7 +2393,7 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
labibb->lab_vreg.vreg_enabled = 1;
}
- if (labibb->notify_lab_vreg_ok_sts)
+ if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc)
schedule_work(&labibb->lab_vreg_ok_work);
return 0;
@@ -2621,6 +2651,12 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node,
"qcom,notify-lab-vreg-ok-sts");
+ labibb->lab_vreg.sc_wait_time_ms = -EINVAL;
+ if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE &&
+ labibb->detect_lab_sc)
+ of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms",
+ &labibb->lab_vreg.sc_wait_time_ms);
+
rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start",
&(labibb->lab_vreg.soft_start));
if (!rc) {
@@ -3255,6 +3291,11 @@ static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
u8 val;
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
+ if (labibb->sc_detected) {
+ pr_info("Short circuit detected: disabled LAB/IBB rails\n");
+ return 0;
+ }
+
if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {
if (!labibb->standalone)
@@ -3731,6 +3772,8 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+ /* Enable polling for LAB short circuit detection for PM660A */
+ labibb->detect_lab_sc = true;
} else {
rc = of_property_read_string(labibb->dev->of_node,
"qcom,qpnp-labibb-mode", &mode_name);
diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c
index c012f373e80e..bee9a3d82eeb 100644
--- a/drivers/regulator/qpnp-oledb-regulator.c
+++ b/drivers/regulator/qpnp-oledb-regulator.c
@@ -27,6 +27,7 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/qpnp-labibb-regulator.h>
#include <linux/qpnp/qpnp-pbs.h>
+#include <linux/qpnp/qpnp-revid.h>
#define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator"
#define OLEDB_VOUT_STEP_MV 100
@@ -162,6 +163,7 @@ struct qpnp_oledb {
struct notifier_block oledb_nb;
struct mutex bus_lock;
struct device_node *pbs_dev_node;
+ struct pmic_revid_data *pmic_rev_id;
u32 base;
u8 mod_enable;
@@ -181,6 +183,8 @@ struct qpnp_oledb {
bool dynamic_ext_pinctl_config;
bool pbs_control;
bool force_pd_control;
+ bool handle_lab_sc_notification;
+ bool lab_sc_detected;
};
static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400};
@@ -275,6 +279,11 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)
struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
+ if (oledb->lab_sc_detected == true) {
+ pr_info("Short circuit detected: Disabled OLEDB rail\n");
+ return 0;
+ }
+
if (oledb->ext_pin_control) {
rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL,
&val, 1);
@@ -368,12 +377,19 @@ static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev)
}
if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) {
- rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node,
- trigger_bitmap);
+ rc = qpnp_oledb_sec_masked_write(oledb, oledb->base +
+ OLEDB_SPARE_CTL,
+ OLEDB_FORCE_PD_CTL_SPARE_BIT, 0);
if (rc < 0) {
- pr_err("Failed to trigger the PBS sequence\n");
+ pr_err("Failed to write SPARE_CTL rc=%d\n", rc);
return rc;
}
+
+ rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node,
+ trigger_bitmap);
+ if (rc < 0)
+ pr_err("Failed to trigger the PBS sequence\n");
+
pr_debug("PBS event triggered\n");
} else {
pr_debug("OLEDB_SPARE_CTL register bit not set\n");
@@ -1085,8 +1101,22 @@ static int qpnp_oledb_parse_fast_precharge(struct qpnp_oledb *oledb)
static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
{
int rc = 0;
+ struct device_node *revid_dev_node;
struct device_node *of_node = oledb->dev->of_node;
+ revid_dev_node = of_parse_phandle(oledb->dev->of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ oledb->pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR(oledb->pmic_rev_id)) {
+ pr_debug("Unable to get revid data\n");
+ return -EPROBE_DEFER;
+ }
+
oledb->swire_control =
of_property_read_bool(of_node, "qcom,swire-control");
@@ -1100,8 +1130,14 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
oledb->pbs_control =
of_property_read_bool(of_node, "qcom,pbs-control");
- oledb->force_pd_control =
- of_property_read_bool(of_node, "qcom,force-pd-control");
+ /* Use the force_pd_control only for PM660A versions <= v2.0 */
+ if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE &&
+ oledb->pmic_rev_id->rev4 <= PM660L_V2P0_REV4) {
+ if (!(oledb->pmic_rev_id->rev4 == PM660L_V2P0_REV4 &&
+ oledb->pmic_rev_id->rev2 > PM660L_V2P0_REV2)) {
+ oledb->force_pd_control = true;
+ }
+ }
if (oledb->force_pd_control) {
oledb->pbs_dev_node = of_parse_phandle(of_node,
@@ -1199,13 +1235,6 @@ static int qpnp_oledb_force_pulldown_config(struct qpnp_oledb *oledb)
int rc = 0;
u8 val;
- rc = qpnp_oledb_sec_masked_write(oledb, oledb->base +
- OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0);
- if (rc < 0) {
- pr_err("Failed to write SPARE_CTL rc=%d\n", rc);
- return rc;
- }
-
val = 1;
rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL,
&val, 1);
@@ -1227,14 +1256,31 @@ static int qpnp_labibb_notifier_cb(struct notifier_block *nb,
unsigned long action, void *data)
{
int rc = 0;
+ u8 val;
struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb,
oledb_nb);
+ if (action == LAB_VREG_NOT_OK) {
+ /* short circuit detected. Disable OLEDB module */
+ val = 0;
+ rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_RDY,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write MODULE_RDY rc=%d\n", rc);
+ return NOTIFY_STOP;
+ }
+ oledb->lab_sc_detected = true;
+ oledb->mod_enable = false;
+ pr_crit("LAB SC detected, disabling OLEDB forever!\n");
+ }
+
if (action == LAB_VREG_OK) {
/* Disable SWIRE pull down control and enable via spmi mode */
rc = qpnp_oledb_force_pulldown_config(oledb);
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("Failed to config force pull down\n");
return NOTIFY_STOP;
+ }
}
return NOTIFY_OK;
@@ -1281,7 +1327,11 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
return rc;
}
- if (oledb->force_pd_control) {
+ /* Enable LAB short circuit notification support */
+ if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+ oledb->handle_lab_sc_notification = true;
+
+ if (oledb->force_pd_control || oledb->handle_lab_sc_notification) {
oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb;
rc = qpnp_labibb_notifier_register(&oledb->oledb_nb);
if (rc < 0) {
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 24ec282e15d8..7c3b8d3516e3 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -1651,6 +1651,9 @@ static void ap_scan_bus(struct work_struct *unused)
ap_dev->queue_depth = queue_depth;
ap_dev->raw_hwtype = device_type;
ap_dev->device_type = device_type;
+ /* CEX6 toleration: map to CEX5 */
+ if (device_type == AP_DEVICE_TYPE_CEX6)
+ ap_dev->device_type = AP_DEVICE_TYPE_CEX5;
ap_dev->functions = device_functions;
spin_lock_init(&ap_dev->lock);
INIT_LIST_HEAD(&ap_dev->pendingq);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 6adcbdf225d1..cc741e948170 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -105,6 +105,7 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
#define AP_DEVICE_TYPE_CEX3C 9
#define AP_DEVICE_TYPE_CEX4 10
#define AP_DEVICE_TYPE_CEX5 11
+#define AP_DEVICE_TYPE_CEX6 12
/*
* Known function facilities
diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h
index 5ada9268a450..a8ac4c0a1493 100644
--- a/drivers/scsi/cxlflash/common.h
+++ b/drivers/scsi/cxlflash/common.h
@@ -34,7 +34,6 @@ extern const struct file_operations cxlflash_cxl_fops;
sectors
*/
-#define NUM_RRQ_ENTRY 16 /* for master issued cmds */
#define MAX_RHT_PER_CONTEXT (PAGE_SIZE / sizeof(struct sisl_rht_entry))
/* AFU command retry limit */
@@ -48,9 +47,12 @@ extern const struct file_operations cxlflash_cxl_fops;
index derivation
*/
-#define CXLFLASH_MAX_CMDS 16
+#define CXLFLASH_MAX_CMDS 256
#define CXLFLASH_MAX_CMDS_PER_LUN CXLFLASH_MAX_CMDS
+/* RRQ for master issued cmds */
+#define NUM_RRQ_ENTRY CXLFLASH_MAX_CMDS
+
static inline void check_sizes(void)
{
@@ -149,7 +151,7 @@ struct afu_cmd {
struct afu {
/* Stuff requiring alignment go first. */
- u64 rrq_entry[NUM_RRQ_ENTRY]; /* 128B RRQ */
+ u64 rrq_entry[NUM_RRQ_ENTRY]; /* 2K RRQ */
/*
* Command & data for AFU commands.
*/
diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c
index c86847c68448..2882bcac918a 100644
--- a/drivers/scsi/cxlflash/main.c
+++ b/drivers/scsi/cxlflash/main.c
@@ -2305,7 +2305,7 @@ static struct scsi_host_template driver_template = {
.eh_device_reset_handler = cxlflash_eh_device_reset_handler,
.eh_host_reset_handler = cxlflash_eh_host_reset_handler,
.change_queue_depth = cxlflash_change_queue_depth,
- .cmd_per_lun = 16,
+ .cmd_per_lun = CXLFLASH_MAX_CMDS_PER_LUN,
.can_queue = CXLFLASH_MAX_CMDS,
.this_id = -1,
.sg_tablesize = SG_NONE, /* No scatter gather support */
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 6bffd91b973a..c1ccf1ee99ea 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -560,8 +560,12 @@ static void iscsi_complete_task(struct iscsi_task *task, int state)
WARN_ON_ONCE(task->state == ISCSI_TASK_FREE);
task->state = state;
- if (!list_empty(&task->running))
+ spin_lock_bh(&conn->taskqueuelock);
+ if (!list_empty(&task->running)) {
+ pr_debug_once("%s while task on list", __func__);
list_del_init(&task->running);
+ }
+ spin_unlock_bh(&conn->taskqueuelock);
if (conn->task == task)
conn->task = NULL;
@@ -783,7 +787,9 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (session->tt->xmit_task(task))
goto free_task;
} else {
+ spin_lock_bh(&conn->taskqueuelock);
list_add_tail(&task->running, &conn->mgmtqueue);
+ spin_unlock_bh(&conn->taskqueuelock);
iscsi_conn_queue_work(conn);
}
@@ -1474,8 +1480,10 @@ void iscsi_requeue_task(struct iscsi_task *task)
* this may be on the requeue list already if the xmit_task callout
* is handling the r2ts while we are adding new ones
*/
+ spin_lock_bh(&conn->taskqueuelock);
if (list_empty(&task->running))
list_add_tail(&task->running, &conn->requeue);
+ spin_unlock_bh(&conn->taskqueuelock);
iscsi_conn_queue_work(conn);
}
EXPORT_SYMBOL_GPL(iscsi_requeue_task);
@@ -1512,22 +1520,26 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
* only have one nop-out as a ping from us and targets should not
* overflow us with nop-ins
*/
+ spin_lock_bh(&conn->taskqueuelock);
check_mgmt:
while (!list_empty(&conn->mgmtqueue)) {
conn->task = list_entry(conn->mgmtqueue.next,
struct iscsi_task, running);
list_del_init(&conn->task->running);
+ spin_unlock_bh(&conn->taskqueuelock);
if (iscsi_prep_mgmt_task(conn, conn->task)) {
/* regular RX path uses back_lock */
spin_lock_bh(&conn->session->back_lock);
__iscsi_put_task(conn->task);
spin_unlock_bh(&conn->session->back_lock);
conn->task = NULL;
+ spin_lock_bh(&conn->taskqueuelock);
continue;
}
rc = iscsi_xmit_task(conn);
if (rc)
goto done;
+ spin_lock_bh(&conn->taskqueuelock);
}
/* process pending command queue */
@@ -1535,19 +1547,24 @@ check_mgmt:
conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task,
running);
list_del_init(&conn->task->running);
+ spin_unlock_bh(&conn->taskqueuelock);
if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
fail_scsi_task(conn->task, DID_IMM_RETRY);
+ spin_lock_bh(&conn->taskqueuelock);
continue;
}
rc = iscsi_prep_scsi_cmd_pdu(conn->task);
if (rc) {
if (rc == -ENOMEM || rc == -EACCES) {
+ spin_lock_bh(&conn->taskqueuelock);
list_add_tail(&conn->task->running,
&conn->cmdqueue);
conn->task = NULL;
+ spin_unlock_bh(&conn->taskqueuelock);
goto done;
} else
fail_scsi_task(conn->task, DID_ABORT);
+ spin_lock_bh(&conn->taskqueuelock);
continue;
}
rc = iscsi_xmit_task(conn);
@@ -1558,6 +1575,7 @@ check_mgmt:
* we need to check the mgmt queue for nops that need to
* be sent to aviod starvation
*/
+ spin_lock_bh(&conn->taskqueuelock);
if (!list_empty(&conn->mgmtqueue))
goto check_mgmt;
}
@@ -1577,12 +1595,15 @@ check_mgmt:
conn->task = task;
list_del_init(&conn->task->running);
conn->task->state = ISCSI_TASK_RUNNING;
+ spin_unlock_bh(&conn->taskqueuelock);
rc = iscsi_xmit_task(conn);
if (rc)
goto done;
+ spin_lock_bh(&conn->taskqueuelock);
if (!list_empty(&conn->mgmtqueue))
goto check_mgmt;
}
+ spin_unlock_bh(&conn->taskqueuelock);
spin_unlock_bh(&conn->session->frwd_lock);
return -ENODATA;
@@ -1738,7 +1759,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
goto prepd_reject;
}
} else {
+ spin_lock_bh(&conn->taskqueuelock);
list_add_tail(&task->running, &conn->cmdqueue);
+ spin_unlock_bh(&conn->taskqueuelock);
iscsi_conn_queue_work(conn);
}
@@ -2900,6 +2923,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
INIT_LIST_HEAD(&conn->mgmtqueue);
INIT_LIST_HEAD(&conn->cmdqueue);
INIT_LIST_HEAD(&conn->requeue);
+ spin_lock_init(&conn->taskqueuelock);
INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
/* allocate login_task used for the login/text sequences */
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index c14ab6c3ae40..60c21093f865 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -11387,6 +11387,7 @@ static struct pci_driver lpfc_driver = {
.id_table = lpfc_id_table,
.probe = lpfc_pci_probe_one,
.remove = lpfc_pci_remove_one,
+ .shutdown = lpfc_pci_remove_one,
.suspend = lpfc_pci_suspend_one,
.resume = lpfc_pci_resume_one,
.err_handler = &lpfc_err_handler,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 79bb3337ba36..c5393d517432 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -9238,10 +9238,11 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
/* scale up to G3 now */
new_pwr_info.gear_tx = UFS_HS_G3;
new_pwr_info.gear_rx = UFS_HS_G3;
- ret = ufshcd_change_power_mode(hba, &new_pwr_info);
- if (ret)
- goto out;
+ /* now, fall through to set the HS-G3 */
}
+ ret = ufshcd_change_power_mode(hba, &new_pwr_info);
+ if (ret)
+ goto out;
} else {
memcpy(&new_pwr_info, &hba->pwr_info,
sizeof(struct ufs_pa_layer_attr));
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 75bebf66376d..34b0adb108eb 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -441,6 +441,23 @@ config QCOM_MEMORY_DUMP_V2
of deadlocks or cpu hangs these dump regions are captured to
give a snapshot of the system at the time of the crash.
+config QCOM_MINIDUMP
+ bool "QCOM Minidump Support"
+ depends on MSM_SMEM && QCOM_DLOAD_MODE
+ help
+ This enables minidump feature. It allows various clients to
+ register to dump their state at system bad state (panic/WDT,etc.,).
+ This uses SMEM to store all registered client information.
+ This will dump all registered entries, only when DLOAD mode is enabled.
+
+config MINIDUMP_MAX_ENTRIES
+ int "Minidump Maximum num of entries"
+ default 200
+ depends on QCOM_MINIDUMP
+ help
+ This defines maximum number of entries to be allocated for application
+ subsytem in Minidump SMEM table.
+
config ICNSS
tristate "Platform driver for Q6 integrated connectivity"
---help---
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index fa350d122384..87698b75d3b8 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_QCOM_SCM_XPU) += scm-xpu.o
obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o
obj-$(CONFIG_QCOM_MEMORY_DUMP) += memory_dump.o
obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o
+obj-$(CONFIG_QCOM_MINIDUMP) += msm_minidump.o
obj-$(CONFIG_QCOM_DCC) += dcc.o
obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o
obj-$(CONFIG_QCOM_COMMON_LOG) += common_log.o
diff --git a/drivers/soc/qcom/common_log.c b/drivers/soc/qcom/common_log.c
index ecf89b2b3b37..f001e820b797 100644
--- a/drivers/soc/qcom/common_log.c
+++ b/drivers/soc/qcom/common_log.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,8 @@
#include <linux/slab.h>
#include <linux/kmemleak.h>
#include <soc/qcom/memory_dump.h>
+#include <soc/qcom/minidump.h>
+#include <asm/sections.h>
#define MISC_DUMP_DATA_LEN 4096
#define PMIC_DUMP_DATA_LEN (64 * 1024)
@@ -38,6 +40,8 @@ void register_misc_dump(void)
misc_buf = kzalloc(MISC_DUMP_DATA_LEN, GFP_KERNEL);
if (!misc_buf)
goto err0;
+
+ strlcpy(misc_data->name, "KMISC", sizeof(misc_data->name));
misc_data->addr = virt_to_phys(misc_buf);
misc_data->len = MISC_DUMP_DATA_LEN;
dump_entry.id = MSM_DUMP_DATA_MISC;
@@ -70,6 +74,7 @@ static void register_pmic_dump(void)
if (!dump_addr)
goto err0;
+ strlcpy(dump_data->name, "KPMIC", sizeof(dump_data->name));
dump_data->addr = virt_to_phys(dump_addr);
dump_data->len = PMIC_DUMP_DATA_LEN;
dump_entry.id = MSM_DUMP_DATA_PMIC;
@@ -104,6 +109,8 @@ static void register_vsense_dump(void)
if (!dump_addr)
goto err0;
+ strlcpy(dump_data->name, "KVSENSE",
+ sizeof(dump_data->name));
dump_data->addr = virt_to_phys(dump_addr);
dump_data->len = VSENSE_DUMP_DATA_LEN;
dump_entry.id = MSM_DUMP_DATA_VSENSE;
@@ -136,6 +143,7 @@ void register_rpm_dump(void)
if (!dump_addr)
goto err0;
+ strlcpy(dump_data->name, "KRPM", sizeof(dump_data->name));
dump_data->addr = virt_to_phys(dump_addr);
dump_data->len = RPM_DUMP_DATA_LEN;
dump_entry.id = MSM_DUMP_DATA_RPM;
@@ -217,8 +225,39 @@ static void __init common_log_register_log_buf(void)
}
}
+static void __init register_kernel_sections(void)
+{
+ struct md_region ksec_entry;
+ char *data_name = "KDATABSS";
+ const size_t static_size = __per_cpu_end - __per_cpu_start;
+ void __percpu *base = (void __percpu *)__per_cpu_start;
+ unsigned int cpu;
+
+ strlcpy(ksec_entry.name, data_name, sizeof(ksec_entry.name));
+ ksec_entry.virt_addr = (uintptr_t)_sdata;
+ ksec_entry.phys_addr = virt_to_phys(_sdata);
+ ksec_entry.size = roundup((__bss_stop - _sdata), 4);
+ if (msm_minidump_add_region(&ksec_entry))
+ pr_err("Failed to add data section in Minidump\n");
+
+ /* Add percpu static sections */
+ for_each_possible_cpu(cpu) {
+ void *start = per_cpu_ptr(base, cpu);
+
+ memset(&ksec_entry, 0, sizeof(ksec_entry));
+ scnprintf(ksec_entry.name, sizeof(ksec_entry.name),
+ "KSPERCPU%d", cpu);
+ ksec_entry.virt_addr = (uintptr_t)start;
+ ksec_entry.phys_addr = per_cpu_ptr_to_phys(start);
+ ksec_entry.size = static_size;
+ if (msm_minidump_add_region(&ksec_entry))
+ pr_err("Failed to add percpu sections in Minidump\n");
+ }
+}
+
static int __init msm_common_log_init(void)
{
+ register_kernel_sections();
common_log_register_log_buf();
register_misc_dump();
register_pmic_dump();
diff --git a/drivers/soc/qcom/cpuss_dump.c b/drivers/soc/qcom/cpuss_dump.c
index 93c876a7ad73..2e08b78dee94 100644
--- a/drivers/soc/qcom/cpuss_dump.c
+++ b/drivers/soc/qcom/cpuss_dump.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -75,6 +75,8 @@ static int cpuss_dump_probe(struct platform_device *pdev)
dump_data->addr = dump_addr;
dump_data->len = size;
+ scnprintf(dump_data->name, sizeof(dump_data->name),
+ "KCPUSS%X", id);
dump_entry.id = id;
dump_entry.addr = virt_to_phys(dump_data);
ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c
index aced50bf7fda..e5f3f119065b 100644
--- a/drivers/soc/qcom/dcc.c
+++ b/drivers/soc/qcom/dcc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1173,6 +1173,8 @@ static void dcc_allocate_dump_mem(struct dcc_drvdata *drvdata)
/* Allocate memory for dcc reg dump */
drvdata->reg_buf = devm_kzalloc(dev, drvdata->reg_size, GFP_KERNEL);
if (drvdata->reg_buf) {
+ strlcpy(drvdata->reg_data.name, "KDCC_REG",
+ sizeof(drvdata->reg_data.name));
drvdata->reg_data.addr = virt_to_phys(drvdata->reg_buf);
drvdata->reg_data.len = drvdata->reg_size;
reg_dump_entry.id = MSM_DUMP_DATA_DCC_REG;
@@ -1190,6 +1192,8 @@ static void dcc_allocate_dump_mem(struct dcc_drvdata *drvdata)
/* Allocate memory for dcc sram dump */
drvdata->sram_buf = devm_kzalloc(dev, drvdata->ram_size, GFP_KERNEL);
if (drvdata->sram_buf) {
+ strlcpy(drvdata->sram_data.name, "KDCC_SRAM",
+ sizeof(drvdata->sram_data.name));
drvdata->sram_data.addr = virt_to_phys(drvdata->sram_buf);
drvdata->sram_data.len = drvdata->ram_size;
sram_dump_entry.id = MSM_DUMP_DATA_DCC_SRAM;
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index cc809cbdd839..72f5829d1eb6 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -1845,7 +1845,7 @@ static void glink_ch_ctx_release(struct rwref_lock *ch_st_lock)
/**
* ch_name_to_ch_ctx_create() - lookup a channel by name, create the channel if
- * it is not found.
+ * it is not found and get reference of context.
* @xprt_ctx: Transport to search for a matching channel.
* @name: Name of the desired channel.
*
@@ -1901,6 +1901,7 @@ check_ctx:
spin_unlock_irqrestore(&xprt_ctx->xprt_ctx_lock_lhb1,
flags);
kfree(ctx);
+ rwref_get(&entry->ch_state_lhb2);
rwref_write_put(&xprt_ctx->xprt_state_lhb0);
return entry;
}
@@ -1935,6 +1936,7 @@ check_ctx:
"%s: local:GLINK_CHANNEL_CLOSED\n",
__func__);
}
+ rwref_get(&ctx->ch_state_lhb2);
spin_unlock_irqrestore(&xprt_ctx->xprt_ctx_lock_lhb1, flags);
rwref_write_put(&xprt_ctx->xprt_state_lhb0);
mutex_lock(&xprt_ctx->xprt_dbgfs_lock_lhb4);
@@ -2579,6 +2581,7 @@ void *glink_open(const struct glink_open_config *cfg)
GLINK_INFO_CH_XPRT(ctx, transport_ptr,
"%s: Channel not ready to be re-opened. State: %u\n",
__func__, ctx->local_open_state);
+ rwref_put(&ctx->ch_state_lhb2);
return ERR_PTR(-EBUSY);
}
@@ -2627,11 +2630,13 @@ void *glink_open(const struct glink_open_config *cfg)
ctx->local_open_state = GLINK_CHANNEL_CLOSED;
GLINK_ERR_CH(ctx, "%s: Unable to send open command %d\n",
__func__, ret);
+ rwref_put(&ctx->ch_state_lhb2);
return ERR_PTR(ret);
}
GLINK_INFO_CH(ctx, "%s: Created channel, sent OPEN command. ctx %p\n",
__func__, ctx);
+ rwref_put(&ctx->ch_state_lhb2);
return ctx;
}
EXPORT_SYMBOL(glink_open);
@@ -4804,6 +4809,7 @@ static void glink_core_rx_cmd_ch_remote_open(struct glink_transport_if *if_ptr,
GLINK_ERR_CH(ctx,
"%s: Duplicate remote open for rcid %u, name '%s'\n",
__func__, rcid, name);
+ rwref_put(&ctx->ch_state_lhb2);
glink_core_migration_edge_unlock(if_ptr->glink_core_priv);
return;
}
@@ -4826,6 +4832,7 @@ static void glink_core_rx_cmd_ch_remote_open(struct glink_transport_if *if_ptr,
if (do_migrate)
ch_migrate(NULL, ctx);
+ rwref_put(&ctx->ch_state_lhb2);
glink_core_migration_edge_unlock(if_ptr->glink_core_priv);
}
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 2dc4208cbc51..3f969234b705 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -678,7 +678,8 @@ static void process_rx_data(struct edge_info *einfo, uint16_t cmd_id,
err = true;
} else if (intent->data == NULL) {
if (einfo->intentless) {
- intent->data = kmalloc(cmd.frag_size, GFP_ATOMIC);
+ intent->data = kmalloc(cmd.frag_size,
+ __GFP_ATOMIC | __GFP_HIGH);
if (!intent->data) {
err = true;
GLINK_ERR(
@@ -798,6 +799,12 @@ static bool get_rx_fifo(struct edge_info *einfo)
einfo->remote_proc_id,
SMEM_ITEM_CACHED_FLAG);
if (!einfo->rx_fifo)
+ einfo->rx_fifo = smem_get_entry(
+ SMEM_GLINK_NATIVE_XPRT_FIFO_1,
+ &einfo->rx_fifo_size,
+ einfo->remote_proc_id,
+ 0);
+ if (!einfo->rx_fifo)
return false;
}
diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c
index af141c808c81..092b1c1af44b 100644
--- a/drivers/soc/qcom/memory_dump_v2.c
+++ b/drivers/soc/qcom/memory_dump_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,7 @@
#include <linux/kmemleak.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/scm.h>
+#include <soc/qcom/minidump.h>
#define MSM_DUMP_TABLE_VERSION MSM_DUMP_MAKE_VERSION(2, 0)
@@ -87,6 +88,29 @@ static struct msm_dump_table *msm_dump_get_table(enum msm_dump_table_ids id)
return table;
}
+int msm_dump_data_add_minidump(struct msm_dump_entry *entry)
+{
+ struct msm_dump_data *data;
+ struct md_region md_entry;
+
+ data = (struct msm_dump_data *)(phys_to_virt(entry->addr));
+ if (!strcmp(data->name, "")) {
+ pr_info("Entry name is NULL, Use ID %d for minidump\n",
+ entry->id);
+ snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X",
+ entry->id);
+ } else {
+ strlcpy(md_entry.name, data->name, sizeof(md_entry.name));
+ }
+
+ md_entry.phys_addr = data->addr;
+ md_entry.virt_addr = (uintptr_t)phys_to_virt(data->addr);
+ md_entry.size = data->len;
+ md_entry.id = entry->id;
+
+ return msm_minidump_add_region(&md_entry);
+}
+
int msm_dump_data_register(enum msm_dump_table_ids id,
struct msm_dump_entry *entry)
{
@@ -107,6 +131,10 @@ int msm_dump_data_register(enum msm_dump_table_ids id,
table->num_entries++;
dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table));
+
+ if (msm_dump_data_add_minidump(entry))
+ pr_info("Failed to add entry in Minidump table\n");
+
return 0;
}
EXPORT_SYMBOL(msm_dump_data_register);
diff --git a/drivers/soc/qcom/msm_minidump.c b/drivers/soc/qcom/msm_minidump.c
new file mode 100644
index 000000000000..1cb36bf98555
--- /dev/null
+++ b/drivers/soc/qcom/msm_minidump.c
@@ -0,0 +1,371 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "Minidump: " fmt
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/elf.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <soc/qcom/smem.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/minidump.h>
+
+
+#define MAX_NUM_ENTRIES (CONFIG_MINIDUMP_MAX_ENTRIES + 1)
+#define SMEM_ENTRY_SIZE 32
+#define MAX_MEM_LENGTH (SMEM_ENTRY_SIZE * MAX_NUM_ENTRIES)
+#define MAX_STRTBL_SIZE (MAX_NUM_ENTRIES * MAX_NAME_LENGTH)
+#define SMEM_MINIDUMP_TABLE_ID 602
+
+/* Bootloader Minidump table */
+struct md_smem_table {
+ u32 version;
+ u32 smem_length;
+ u64 next_avail_offset;
+ char reserved[MAX_NAME_LENGTH];
+ u64 *region_start;
+};
+
+/* Bootloader Minidump region */
+struct md_smem_region {
+ char name[MAX_NAME_LENGTH];
+ u64 address;
+ u64 size;
+};
+
+/* md_table: APPS minidump table
+ * @num_regions: Number of entries registered
+ * @region_base_offset: APPS region start offset smem table
+ * @md_smem_table: Pointer smem table
+ * @region: Pointer to APPS region in smem table
+ * @entry: All registered client entries.
+ */
+
+struct md_table {
+ u32 num_regions;
+ u32 region_base_offset;
+ struct md_smem_table *md_smem_table;
+ struct md_smem_region *region;
+ struct md_region entry[MAX_NUM_ENTRIES];
+};
+
+/* Protect elfheader and smem table from deferred calls contention */
+static DEFINE_SPINLOCK(mdt_lock);
+static bool minidump_enabled;
+static struct md_table minidump_table;
+static unsigned int pendings;
+static unsigned int region_idx = 1; /* First entry is ELF header*/
+
+/* ELF Header */
+static struct elfhdr *md_ehdr;
+/* ELF Program header */
+static struct elf_phdr *phdr;
+/* ELF Section header */
+static struct elf_shdr *shdr;
+/* Section offset in elf image */
+static u64 elf_offset;
+/* String table index, first byte must be '\0' */
+static unsigned int stringtable_idx = 1;
+
+static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr)
+{
+ return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff);
+}
+
+static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx)
+{
+ return &elf_sheader(hdr)[idx];
+}
+
+static inline char *elf_str_table(struct elfhdr *hdr)
+{
+ if (hdr->e_shstrndx == SHN_UNDEF)
+ return NULL;
+ return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset;
+}
+
+static inline char *elf_lookup_string(struct elfhdr *hdr, int offset)
+{
+ char *strtab = elf_str_table(hdr);
+
+ if ((strtab == NULL) | (stringtable_idx < offset))
+ return NULL;
+ return strtab + offset;
+}
+
+static inline unsigned int set_section_name(const char *name)
+{
+ char *strtab = elf_str_table(md_ehdr);
+ int ret = 0;
+
+ if ((strtab == NULL) | (name == NULL))
+ return 0;
+
+ ret = stringtable_idx;
+ stringtable_idx += strlcpy((strtab + stringtable_idx),
+ name, MAX_NAME_LENGTH);
+ stringtable_idx += 1;
+ return ret;
+}
+
+/* return 1 if name already exists */
+static inline bool md_check_name(const char *name)
+{
+ struct md_region *mde = minidump_table.entry;
+ int i, regno = minidump_table.num_regions;
+
+ for (i = 0; i < regno; i++, mde++)
+ if (!strcmp(mde->name, name))
+ return true;
+ return false;
+}
+
+/* Update Mini dump table in SMEM */
+static int md_update_smem_table(const struct md_region *entry)
+{
+ struct md_smem_region *mdr;
+
+ if (!minidump_enabled) {
+ pr_info("Table in smem is not setup\n");
+ return -ENODEV;
+ }
+
+ mdr = &minidump_table.region[region_idx++];
+
+ strlcpy(mdr->name, entry->name, sizeof(mdr->name));
+ mdr->address = entry->phys_addr;
+ mdr->size = entry->size;
+
+ /* Update elf header */
+ shdr->sh_type = SHT_PROGBITS;
+ shdr->sh_name = set_section_name(mdr->name);
+ shdr->sh_addr = (elf_addr_t)entry->virt_addr;
+ shdr->sh_size = mdr->size;
+ shdr->sh_flags = SHF_WRITE;
+ shdr->sh_offset = elf_offset;
+ shdr->sh_entsize = 0;
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = elf_offset;
+ phdr->p_vaddr = entry->virt_addr;
+ phdr->p_paddr = entry->phys_addr;
+ phdr->p_filesz = phdr->p_memsz = mdr->size;
+ phdr->p_flags = PF_R | PF_W;
+
+ md_ehdr->e_shnum += 1;
+ md_ehdr->e_phnum += 1;
+ elf_offset += shdr->sh_size;
+ shdr++;
+ phdr++;
+
+ return 0;
+}
+
+bool msm_minidump_enabled(void)
+{
+ bool ret;
+
+ spin_lock(&mdt_lock);
+ ret = minidump_enabled;
+ spin_unlock(&mdt_lock);
+ return ret;
+}
+EXPORT_SYMBOL(msm_minidump_enabled);
+
+int msm_minidump_add_region(const struct md_region *entry)
+{
+ u32 entries;
+ struct md_region *mdr;
+ int ret = 0;
+
+ if (!entry)
+ return -EINVAL;
+
+ if (((strlen(entry->name) > MAX_NAME_LENGTH) ||
+ md_check_name(entry->name)) && !entry->virt_addr) {
+ pr_info("Invalid entry details\n");
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(entry->size, 4)) {
+ pr_info("size should be 4 byte aligned\n");
+ return -EINVAL;
+ }
+
+ spin_lock(&mdt_lock);
+ entries = minidump_table.num_regions;
+ if (entries >= MAX_NUM_ENTRIES) {
+ pr_info("Maximum entries reached.\n");
+ spin_unlock(&mdt_lock);
+ return -ENOMEM;
+ }
+
+ mdr = &minidump_table.entry[entries];
+ strlcpy(mdr->name, entry->name, sizeof(mdr->name));
+ mdr->virt_addr = entry->virt_addr;
+ mdr->phys_addr = entry->phys_addr;
+ mdr->size = entry->size;
+ mdr->id = entry->id;
+
+ minidump_table.num_regions = entries + 1;
+
+ if (minidump_enabled)
+ ret = md_update_smem_table(entry);
+ else
+ pendings++;
+
+ spin_unlock(&mdt_lock);
+
+ pr_debug("Minidump: added %s to %s list\n",
+ mdr->name, minidump_enabled ? "":"pending");
+ return ret;
+}
+EXPORT_SYMBOL(msm_minidump_add_region);
+
+static int msm_minidump_add_header(void)
+{
+ struct md_smem_region *mdreg = &minidump_table.region[0];
+ char *banner;
+ unsigned int strtbl_off, elfh_size, phdr_off;
+
+ elfh_size = sizeof(*md_ehdr) + MAX_STRTBL_SIZE + MAX_MEM_LENGTH +
+ ((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 1));
+
+ md_ehdr = kzalloc(elfh_size, GFP_KERNEL);
+ if (!md_ehdr)
+ return -ENOMEM;
+
+ strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name));
+ mdreg->address = virt_to_phys(md_ehdr);
+ mdreg->size = elfh_size;
+
+ /* Section headers*/
+ shdr = (struct elf_shdr *)(md_ehdr + 1);
+ phdr = (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES);
+ phdr_off = sizeof(*md_ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);
+
+ memcpy(md_ehdr->e_ident, ELFMAG, SELFMAG);
+ md_ehdr->e_ident[EI_CLASS] = ELF_CLASS;
+ md_ehdr->e_ident[EI_DATA] = ELF_DATA;
+ md_ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ md_ehdr->e_ident[EI_OSABI] = ELF_OSABI;
+ md_ehdr->e_type = ET_CORE;
+ md_ehdr->e_machine = ELF_ARCH;
+ md_ehdr->e_version = EV_CURRENT;
+ md_ehdr->e_ehsize = sizeof(*md_ehdr);
+ md_ehdr->e_phoff = phdr_off;
+ md_ehdr->e_phentsize = sizeof(*phdr);
+ md_ehdr->e_phnum = 1;
+ md_ehdr->e_shoff = sizeof(*md_ehdr);
+ md_ehdr->e_shentsize = sizeof(*shdr);
+ md_ehdr->e_shnum = 3; /* NULL, STR TABLE, Linux banner */
+ md_ehdr->e_shstrndx = 1;
+
+ elf_offset = elfh_size;
+ strtbl_off = sizeof(*md_ehdr) +
+ ((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES);
+ /* First section header should be NULL
+ * 2nd entry for string table
+ */
+ shdr++;
+ shdr->sh_type = SHT_STRTAB;
+ shdr->sh_offset = (elf_addr_t)strtbl_off;
+ shdr->sh_size = MAX_STRTBL_SIZE;
+ shdr->sh_entsize = 0;
+ shdr->sh_flags = 0;
+ shdr->sh_name = set_section_name("STR_TBL");
+ shdr++;
+
+ /* 3rd entry for linux banner */
+ banner = (char *)md_ehdr + strtbl_off + MAX_STRTBL_SIZE;
+ strlcpy(banner, linux_banner, MAX_MEM_LENGTH);
+
+ shdr->sh_type = SHT_PROGBITS;
+ shdr->sh_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
+ shdr->sh_size = strlen(linux_banner) + 1;
+ shdr->sh_addr = (elf_addr_t)linux_banner;
+ shdr->sh_entsize = 0;
+ shdr->sh_flags = SHF_WRITE;
+ shdr->sh_name = set_section_name("linux_banner");
+ shdr++;
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
+ phdr->p_vaddr = (elf_addr_t)linux_banner;
+ phdr->p_paddr = virt_to_phys(linux_banner);
+ phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1;
+ phdr->p_flags = PF_R | PF_W;
+
+ md_ehdr->e_phnum += 1;
+ phdr++;
+
+ return 0;
+}
+
+static int __init msm_minidump_init(void)
+{
+ unsigned int i, size;
+ struct md_region *mdr;
+ struct md_smem_table *smem_table;
+
+ /* Get Minidump table */
+ smem_table = smem_get_entry(SMEM_MINIDUMP_TABLE_ID, &size, 0,
+ SMEM_ANY_HOST_FLAG);
+ if (IS_ERR_OR_NULL(smem_table)) {
+ pr_info("SMEM is not initialized.\n");
+ return -ENODEV;
+ }
+
+ if ((smem_table->next_avail_offset + MAX_MEM_LENGTH) >
+ smem_table->smem_length) {
+ pr_info("SMEM memory not available.\n");
+ return -ENOMEM;
+ }
+
+ /* Get next_avail_offset and update it to reserve memory */
+ minidump_table.region_base_offset = smem_table->next_avail_offset;
+ minidump_table.region = (struct md_smem_region *)((uintptr_t)smem_table
+ + minidump_table.region_base_offset);
+
+ smem_table->next_avail_offset =
+ minidump_table.region_base_offset + MAX_MEM_LENGTH;
+ minidump_table.md_smem_table = smem_table;
+
+ msm_minidump_add_header();
+
+ /* Add pending entries to smem table */
+ spin_lock(&mdt_lock);
+ minidump_enabled = true;
+
+ for (i = 0; i < pendings; i++) {
+ mdr = &minidump_table.entry[i];
+ if (md_update_smem_table(mdr)) {
+ pr_info("Unable to add entry %s to smem table\n",
+ mdr->name);
+ spin_unlock(&mdt_lock);
+ return -ENODEV;
+ }
+ }
+
+ pendings = 0;
+ spin_unlock(&mdt_lock);
+
+ pr_info("Enabled, region base:%d, region 0x%pK\n",
+ minidump_table.region_base_offset, minidump_table.region);
+
+ return 0;
+}
+subsys_initcall(msm_minidump_init)
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 53bddc5987df..988b6e8c9fd9 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -616,7 +616,15 @@ int pil_mss_reset_load_mba(struct pil_desc *pil)
/* Load the MBA image into memory */
count = fw->size;
- memcpy(mba_dp_virt, data, count);
+ if (count <= SZ_1M) {
+ /* Ensures memcpy is done for max 1MB fw size */
+ memcpy(mba_dp_virt, data, count);
+ } else {
+ dev_err(pil->dev, "%s fw image loading into memory is failed due to fw size overflow\n",
+ __func__);
+ ret = -EINVAL;
+ goto err_mba_data;
+ }
/* Ensure memcpy of the MBA memory is done before loading the DP */
wmb();
diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
index 3a6d84140bc9..8a2be787b70e 100644
--- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
+++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
@@ -114,7 +114,7 @@ int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
{
int rc = 0, retries = 0;
void *pkt_data = NULL;
- struct apr_tx_buf *tx_buf;
+ struct apr_tx_buf *tx_buf = NULL;
struct apr_pkt_priv *pkt_priv_ptr = pkt_priv;
if (!apr_ch->handle || !pkt_priv)
diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/drivers/soc/qcom/qdsp6v2/audio_notifier.c
index a59b436234c7..b46cd2067441 100644
--- a/drivers/soc/qcom/qdsp6v2/audio_notifier.c
+++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c
@@ -510,7 +510,7 @@ int audio_notifier_deregister(char *client_name)
int ret = 0;
int ret2;
struct list_head *ptr, *next;
- struct client_data *client_data;
+ struct client_data *client_data = NULL;
if (client_name == NULL) {
pr_err("%s: client_name is NULL\n", __func__);
diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c
index c86eebcd390f..70cf11359e97 100644
--- a/drivers/soc/qcom/qpnp-haptic.c
+++ b/drivers/soc/qcom/qpnp-haptic.c
@@ -138,7 +138,7 @@
#define QPNP_HAP_WAV_SAMP_MAX 0x7E
#define QPNP_HAP_BRAKE_PAT_LEN 4
#define QPNP_HAP_PLAY_EN 0x80
-#define QPNP_HAP_EN 0x80
+#define QPNP_HAP_EN_BIT BIT(7)
#define QPNP_HAP_BRAKE_MASK BIT(0)
#define QPNP_HAP_AUTO_RES_MASK BIT(7)
#define AUTO_RES_ENABLE BIT(7)
@@ -305,7 +305,6 @@ struct qpnp_pwm_info {
* @ wave_samp - array of wave samples
* @ shadow_wave_samp - shadow array of wave samples
* @ brake_pat - pattern for active breaking
- * @ reg_en_ctl - enable control register
* @ reg_play - play register
* @ lra_res_cal_period - period for resonance calibration
* @ sc_duration - counter to determine the duration of short circuit condition
@@ -358,6 +357,7 @@ struct qpnp_hap {
u32 sc_irq;
u32 status_flags;
u16 base;
+ u16 last_rate_cfg;
u16 drive_period_code_max_limit;
u16 drive_period_code_min_limit;
u16 lra_res_cal_period;
@@ -368,7 +368,6 @@ struct qpnp_hap {
u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
- u8 reg_en_ctl;
u8 reg_play;
u8 sc_duration;
u8 ext_pwm_dtest_line;
@@ -391,6 +390,18 @@ struct qpnp_hap {
static struct qpnp_hap *ghap;
/* helper to read a pmic register */
+static int qpnp_hap_read_mult_reg(struct qpnp_hap *hap, u16 addr, u8 *val,
+ int len)
+{
+ int rc;
+
+ rc = regmap_bulk_read(hap->regmap, addr, val, len);
+ if (rc < 0)
+ pr_err("Error reading address: %X - ret %X\n", addr, rc);
+
+ return rc;
+}
+
static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val)
{
int rc;
@@ -399,11 +410,28 @@ static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val)
rc = regmap_read(hap->regmap, addr, &tmp);
if (rc < 0)
pr_err("Error reading address: %X - ret %X\n", addr, rc);
- *val = (u8)tmp;
+ else
+ *val = (u8)tmp;
+
return rc;
}
/* helper to write a pmic register */
+static int qpnp_hap_write_mult_reg(struct qpnp_hap *hap, u16 addr, u8 *val,
+ int len)
+{
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&hap->bus_lock, flags);
+ rc = regmap_bulk_write(hap->regmap, addr, val, len);
+ if (rc < 0)
+ pr_err("Error writing address: %X - ret %X\n", addr, rc);
+
+ spin_unlock_irqrestore(&hap->bus_lock, flags);
+ return rc;
+}
+
static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val)
{
unsigned long flags;
@@ -480,15 +508,12 @@ static void qpnp_handle_sc_irq(struct work_struct *work)
}
}
-static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
+static int qpnp_hap_mod_enable(struct qpnp_hap *hap, bool on)
{
u8 val;
int rc, i;
- val = hap->reg_en_ctl;
- if (on) {
- val |= QPNP_HAP_EN;
- } else {
+ if (!on) {
for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) {
/* wait for 4 cycles of play rate */
unsigned long sleep_time =
@@ -511,16 +536,13 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
if (i >= QPNP_HAP_MAX_RETRIES)
pr_debug("Haptics Busy. Force disable\n");
-
- val &= ~QPNP_HAP_EN;
}
+ val = on ? QPNP_HAP_EN_BIT : 0;
rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val);
if (rc < 0)
return rc;
- hap->reg_en_ctl = val;
-
return 0;
}
@@ -1517,20 +1539,23 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
static void update_lra_frequency(struct qpnp_hap *hap)
{
- u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0, val;
+ u8 lra_auto_res[2], val;
u32 play_rate_code;
+ u16 rate_cfg;
int rc;
- qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base),
- &lra_auto_res_lo);
- qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_HI(hap->base),
- &lra_auto_res_hi);
+ rc = qpnp_hap_read_mult_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base),
+ lra_auto_res, 2);
+ if (rc < 0) {
+ pr_err("Error in reading LRA_AUTO_RES_LO/HI, rc=%d\n", rc);
+ return;
+ }
play_rate_code =
- (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF);
+ (lra_auto_res[1] & 0xF0) << 4 | (lra_auto_res[0] & 0xFF);
pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n",
- lra_auto_res_lo, lra_auto_res_hi, play_rate_code);
+ lra_auto_res[0], lra_auto_res[1], play_rate_code);
rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
if (rc < 0)
@@ -1559,12 +1584,21 @@ static void update_lra_frequency(struct qpnp_hap *hap)
return;
}
- qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base),
- lra_auto_res_lo);
+ lra_auto_res[1] >>= 4;
+ rate_cfg = lra_auto_res[1] << 8 | lra_auto_res[0];
+ if (hap->last_rate_cfg == rate_cfg) {
+ pr_debug("Same rate_cfg, skip updating\n");
+ return;
+ }
- lra_auto_res_hi = lra_auto_res_hi >> 4;
- qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base),
- lra_auto_res_hi);
+ rc = qpnp_hap_write_mult_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base),
+ lra_auto_res, 2);
+ if (rc < 0) {
+ pr_err("Error in writing to RATE_CFG1/2, rc=%d\n", rc);
+ } else {
+ pr_debug("Update RATE_CFG with [0x%x]\n", rate_cfg);
+ hap->last_rate_cfg = rate_cfg;
+ }
}
static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
@@ -1983,6 +2017,8 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
if (rc)
return rc;
+ hap->last_rate_cfg = hap->init_drive_period_code;
+
if (hap->act_type == QPNP_HAP_LRA &&
hap->perform_lra_auto_resonance_search)
calculate_lra_code(hap);
@@ -2019,12 +2055,6 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
return rc;
}
- /* Cache enable control register */
- rc = qpnp_hap_read_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), &val);
- if (rc < 0)
- return rc;
- hap->reg_en_ctl = val;
-
/* Cache play register */
rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val);
if (rc < 0)
diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c
index f2784dedbc7a..e792c953f31f 100644
--- a/drivers/soc/qcom/rpm-smd.c
+++ b/drivers/soc/qcom/rpm-smd.c
@@ -108,9 +108,7 @@ static struct glink_apps_rpm_data *glink_data;
#define RPM_DATA_LEN_SIZE 16
#define RPM_HDR_SIZE ((rpm_msg_fmt_ver == RPM_MSG_V0_FMT) ?\
sizeof(struct rpm_v0_hdr) : sizeof(struct rpm_v1_hdr))
-#define GET_FIELD(offset, size) (((1U << (offset + size)) - 1) - \
- ((1U << offset) - 1))
-#define CLEAR_FIELD(offset, size) (~GET_FIELD(offset, size))
+#define CLEAR_FIELD(offset, size) (~GENMASK(offset + size - 1, offset))
static ATOMIC_NOTIFIER_HEAD(msm_rpm_sleep_notifier);
static bool standalone;
@@ -223,7 +221,7 @@ static uint32_t msm_rpm_get_next_msg_id(void);
static inline uint32_t get_offset_value(uint32_t val, uint32_t offset,
uint32_t size)
{
- return (((val) & GET_FIELD(offset, size))
+ return (((val) & GENMASK(offset + size - 1, offset))
>> offset);
}
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index 045a5001fc9f..36804b988d62 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -654,7 +654,7 @@ int scm_call2(u32 fn_id, struct scm_desc *desc)
if (unlikely(!is_scm_armv8()))
return -ENODEV;
- ret = allocate_extra_arg_buffer(desc, GFP_KERNEL);
+ ret = allocate_extra_arg_buffer(desc, GFP_NOIO);
if (ret)
return ret;
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 0625f75de373..97cd11201262 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -24,7 +24,6 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
-#include <linux/debugfs.h>
#include <soc/qcom/msm_qmi_interface.h>
#include <soc/qcom/service-locator.h>
@@ -440,140 +439,3 @@ int find_subsys(const char *pd_path, char *subsys)
return 0;
}
EXPORT_SYMBOL(find_subsys);
-
-static struct pd_qmi_client_data test_data;
-
-static int servloc_test_pdr_cb(struct notifier_block *this,
- unsigned long opcode, void *ptr)
-{
- int i, rc = 0;
- char subsys[QMI_SERVREG_LOC_NAME_LENGTH_V01];
- struct pd_qmi_client_data *return_data;
-
- return_data = (struct pd_qmi_client_data *)ptr;
-
- if (opcode) {
- pr_err("%s: Failed to get process domain!, opcode = %lu\n",
- __func__, opcode);
- return -EIO;
- }
-
- pr_err("Service Name: %s\tTotal Domains: %d\n",
- return_data->service_name, return_data->total_domains);
-
- for (i = 0; i < return_data->total_domains; i++) {
- pr_err("Instance ID: %d\t ",
- return_data->domain_list[i].instance_id);
- pr_err("Domain Name: %s\n",
- return_data->domain_list[i].name);
- rc = find_subsys(return_data->domain_list[i].name,
- subsys);
- if (rc < 0)
- pr_err("No valid subsys found for %s!\n",
- return_data->domain_list[i].name);
- else
- pr_err("Subsys: %s\n", subsys);
- }
- return 0;
-}
-
-static struct notifier_block pdr_service_nb = {
- .notifier_call = servloc_test_pdr_cb,
-};
-
-static ssize_t servloc_read(struct file *filp, char __user *ubuf,
- size_t cnt, loff_t *ppos)
-{
- int rc = 0;
- char *node_name = filp->private_data;
-
- if (!strcmp(node_name, "test_servloc_get"))
- rc = get_service_location(test_data.client_name,
- test_data.service_name, &pdr_service_nb);
-
- return rc;
-}
-
-static ssize_t servloc_write(struct file *fp, const char __user *buf,
- size_t count, loff_t *unused)
-{
- char *node_name = fp->private_data;
-
- if (!buf)
- return -EIO;
- if (!strcmp(node_name, "service_name")) {
- snprintf(test_data.service_name, sizeof(test_data.service_name),
- "%.*s", (int) min((size_t)count - 1,
- (sizeof(test_data.service_name) - 1)), buf);
- } else {
- snprintf(test_data.client_name, sizeof(test_data.client_name),
- "%.*s", (int) min((size_t)count - 1,
- (sizeof(test_data.client_name) - 1)), buf);
- }
- return count;
-}
-
-static const struct file_operations servloc_fops = {
- .open = simple_open,
- .read = servloc_read,
- .write = servloc_write,
-};
-
-static struct dentry *servloc_base_dir;
-static struct dentry *test_servloc_file;
-
-static int __init servloc_debugfs_init(void)
-{
- servloc_base_dir = debugfs_create_dir("test_servloc", NULL);
- return !servloc_base_dir ? -ENOMEM : 0;
-}
-
-static void servloc_debugfs_exit(void)
-{
- debugfs_remove_recursive(servloc_base_dir);
-}
-
-static int servloc_debugfs_add(void)
-{
- int rc;
-
- if (!servloc_base_dir)
- return -ENOMEM;
-
- test_servloc_file = debugfs_create_file("client_name",
- S_IRUGO | S_IWUSR, servloc_base_dir,
- "client_name", &servloc_fops);
- rc = !test_servloc_file ? -ENOMEM : 0;
-
- if (rc == 0) {
- test_servloc_file = debugfs_create_file("service_name",
- S_IRUGO | S_IWUSR, servloc_base_dir,
- "service_name", &servloc_fops);
- rc = !test_servloc_file ? -ENOMEM : 0;
- }
-
- if (rc == 0) {
- test_servloc_file = debugfs_create_file("test_servloc_get",
- S_IRUGO | S_IWUSR, servloc_base_dir,
- "test_servloc_get", &servloc_fops);
- rc = !test_servloc_file ? -ENOMEM : 0;
- }
- return rc;
-}
-
-static int __init service_locator_init(void)
-{
- pr_debug("service_locator_status = %d\n", locator_status);
- if (servloc_debugfs_init())
- pr_err("Could not create test_servloc base directory!");
- if (servloc_debugfs_add())
- pr_err("Could not create test_servloc node entries!");
- return 0;
-}
-
-static void __exit service_locator_exit(void)
-{
- servloc_debugfs_exit();
-}
-module_init(service_locator_init);
-module_exit(service_locator_exit);
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index fa916ac5ade4..68592feccb33 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -21,7 +21,6 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/err.h>
-#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <soc/qcom/subsystem_restart.h>
@@ -752,179 +751,3 @@ int service_notif_unregister_notifier(void *service_notif_handle,
&service_notif->service_notif_rcvr_list, nb);
}
EXPORT_SYMBOL(service_notif_unregister_notifier);
-
-struct service_notifier_test_data {
- char service_path[MAX_STRING_LEN];
- int instance_id;
- struct notifier_block nb;
- void *service_notif_handle;
-};
-
-static struct service_notifier_test_data test_data;
-
-static void print_service_provider_state(int notification, char *type)
-{
- if (notification == SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
- pr_info("%s: Service %s down!\n", type, test_data.service_path);
- else if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
- pr_info("%s: Service %s up!\n", type, test_data.service_path);
- else if (notification == SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01)
- pr_info("%s: Service %s state uninit!\n", type,
- test_data.service_path);
- else
- pr_info("%s: Service %s state Unknown 0x%x!\n", type,
- test_data.service_path, notification);
-}
-
-static int nb_callback(struct notifier_block *nb,
- unsigned long notification,
- void *data)
-{
- print_service_provider_state((int)notification, "Notification:");
- return 0;
-}
-
-static ssize_t show_service_path(struct seq_file *f, void *unused)
-{
- if (test_data.service_notif_handle)
- seq_printf(f, "Service Path: %s\n", test_data.service_path);
- else
- seq_puts(f, "No existing notifier\n");
- return 0;
-}
-
-
-static ssize_t set_service_notifier_register(struct file *fp,
- const char __user *buf,
- size_t count, loff_t *ppos)
-{
- int curr_state = INT_MAX, rc;
-
- if (!buf)
- return -EIO;
- if (test_data.service_notif_handle) {
- service_notif_unregister_notifier(
- test_data.service_notif_handle,
- &test_data.nb);
- test_data.service_notif_handle = NULL;
- pr_info("Unregistering existing notifier for %s\n",
- test_data.service_path);
- }
- rc = simple_write_to_buffer(test_data.service_path, MAX_STRING_LEN,
- ppos, buf, count - 1);
- if (rc != count - 1) {
- pr_err("Unable to read data into kernel buffer\n");
- goto err;
- }
- test_data.nb.notifier_call = nb_callback;
- test_data.service_notif_handle = service_notif_register_notifier(
- test_data.service_path,
- test_data.instance_id, &test_data.nb,
- &curr_state);
- if (!IS_ERR(test_data.service_notif_handle)) {
- pr_info("Notifier Registered for service %s\n",
- test_data.service_path);
- print_service_provider_state(curr_state, "Initial State");
- return count;
- }
-err:
- test_data.service_notif_handle = NULL;
- pr_err("Unable to register notifier for %s\n", test_data.service_path);
- return -EIO;
-}
-
-static int open_service_notifier_register(struct inode *inode, struct file *f)
-{
- return single_open(f, (void *) show_service_path,
- inode->i_private);
-}
-
-static const struct file_operations service_notifier_register_fops = {
- .open = open_service_notifier_register,
- .read = seq_read,
- .write = set_service_notifier_register,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-static ssize_t show_service_notifier_id(struct seq_file *f, void *unused)
-{
- seq_printf(f, "Service instance ID: %d\n", test_data.instance_id);
- return 0;
-}
-
-static ssize_t set_service_notifier_id(struct file *fp,
- const char __user *buf,
- size_t count, loff_t *unused)
-{
- int val, rc;
- char kbuf[MAX_STRING_LEN];
-
- if (count > MAX_STRING_LEN) {
- rc = -EIO;
- goto err;
- }
- rc = copy_from_user(kbuf, buf, count);
- if (rc != 0) {
- rc = -EFAULT;
- goto err;
- }
-
- kbuf[count - 1] = '\0';
- rc = kstrtoint(kbuf, 0, &val);
- if (rc < 0)
- goto err;
-
- test_data.instance_id = val;
- return count;
-err:
- pr_err("Invalid input parameters: rc = %d\n", rc);
- return rc;
-}
-
-static int open_service_notifier_id(struct inode *inode, struct file *f)
-{
- return single_open(f, (void *) show_service_notifier_id,
- inode->i_private);
-}
-
-static const struct file_operations service_notifier_id_fops = {
- .open = open_service_notifier_id,
- .read = seq_read,
- .write = set_service_notifier_id,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-static struct dentry *service_notifier_dir;
-static struct dentry *service_path_file;
-static struct dentry *service_id_file;
-
-static int __init service_notifier_init(void)
-{
- service_notifier_dir = debugfs_create_dir("service_notifier", NULL);
- if (service_notifier_dir) {
- service_path_file = debugfs_create_file("service_path",
- S_IRUGO | S_IWUSR, service_notifier_dir, NULL,
- &service_notifier_register_fops);
- if (!service_path_file)
- goto err;
- service_id_file = debugfs_create_file("service_id",
- S_IRUGO | S_IWUSR, service_notifier_dir, NULL,
- &service_notifier_id_fops);
- if (!service_id_file)
- goto err;
- }
- return 0;
-err:
- debugfs_remove_recursive(service_notifier_dir);
- return 0;
-}
-
-static void __exit service_notifier_exit(void)
-{
- debugfs_remove_recursive(service_notifier_dir);
- test_data.nb.notifier_call = nb_callback;
-}
-module_init(service_notifier_init);
-module_exit(service_notifier_exit);
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index dd3e545eb7da..c1d8748a5d08 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -46,6 +46,7 @@
#define SMEM_IMAGE_VERSION_OEM_OFFSET 95
#define SMEM_IMAGE_VERSION_PARTITION_APPS 10
+static DECLARE_RWSEM(current_image_rwsem);
enum {
HW_PLATFORM_UNKNOWN = 0,
HW_PLATFORM_SURF = 1,
@@ -530,7 +531,7 @@ static struct msm_soc_info cpu_of_id[] = {
/* Cobalt IDs */
[292] = {MSM_CPU_8998, "MSM8998"},
- [319] = {MSM_CPU_8998, "APQ8998"},
+ [319] = {MSM_CPU_8998, "APQ8098"},
/* Hamster ID */
[306] = {MSM_CPU_HAMSTER, "MSMHAMSTER"},
@@ -899,7 +900,9 @@ msm_get_image_version(struct device *dev,
pr_err("Failed to get image version base address");
return snprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "Unknown");
}
+ down_read(&current_image_rwsem);
string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+ up_read(&current_image_rwsem);
return snprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "%-.75s\n",
string_address);
}
@@ -912,14 +915,19 @@ msm_set_image_version(struct device *dev,
{
char *store_address;
- if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS)
+ down_read(&current_image_rwsem);
+ if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) {
+ up_read(&current_image_rwsem);
return count;
+ }
store_address = socinfo_get_image_version_base_address();
if (IS_ERR_OR_NULL(store_address)) {
pr_err("Failed to get image version base address");
+ up_read(&current_image_rwsem);
return count;
}
store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+ up_read(&current_image_rwsem);
snprintf(store_address, SMEM_IMAGE_VERSION_NAME_SIZE, "%-.75s", buf);
return count;
}
@@ -937,7 +945,9 @@ msm_get_image_variant(struct device *dev,
return snprintf(buf, SMEM_IMAGE_VERSION_VARIANT_SIZE,
"Unknown");
}
+ down_read(&current_image_rwsem);
string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+ up_read(&current_image_rwsem);
string_address += SMEM_IMAGE_VERSION_VARIANT_OFFSET;
return snprintf(buf, SMEM_IMAGE_VERSION_VARIANT_SIZE, "%-.20s\n",
string_address);
@@ -951,14 +961,19 @@ msm_set_image_variant(struct device *dev,
{
char *store_address;
- if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS)
+ down_read(&current_image_rwsem);
+ if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) {
+ up_read(&current_image_rwsem);
return count;
+ }
store_address = socinfo_get_image_version_base_address();
if (IS_ERR_OR_NULL(store_address)) {
pr_err("Failed to get image version base address");
+ up_read(&current_image_rwsem);
return count;
}
store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+ up_read(&current_image_rwsem);
store_address += SMEM_IMAGE_VERSION_VARIANT_OFFSET;
snprintf(store_address, SMEM_IMAGE_VERSION_VARIANT_SIZE, "%-.20s", buf);
return count;
@@ -976,7 +991,9 @@ msm_get_image_crm_version(struct device *dev,
pr_err("Failed to get image version base address");
return snprintf(buf, SMEM_IMAGE_VERSION_OEM_SIZE, "Unknown");
}
+ down_read(&current_image_rwsem);
string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+ up_read(&current_image_rwsem);
string_address += SMEM_IMAGE_VERSION_OEM_OFFSET;
return snprintf(buf, SMEM_IMAGE_VERSION_OEM_SIZE, "%-.33s\n",
string_address);
@@ -990,14 +1007,19 @@ msm_set_image_crm_version(struct device *dev,
{
char *store_address;
- if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS)
+ down_read(&current_image_rwsem);
+ if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) {
+ up_read(&current_image_rwsem);
return count;
+ }
store_address = socinfo_get_image_version_base_address();
if (IS_ERR_OR_NULL(store_address)) {
pr_err("Failed to get image version base address");
+ up_read(&current_image_rwsem);
return count;
}
store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+ up_read(&current_image_rwsem);
store_address += SMEM_IMAGE_VERSION_OEM_OFFSET;
snprintf(store_address, SMEM_IMAGE_VERSION_OEM_SIZE, "%-.33s", buf);
return count;
@@ -1008,8 +1030,14 @@ msm_get_image_number(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n",
+ int ret;
+
+ down_read(&current_image_rwsem);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n",
current_image);
+ up_read(&current_image_rwsem);
+ return ret;
+
}
static ssize_t
@@ -1021,10 +1049,12 @@ msm_select_image(struct device *dev, struct device_attribute *attr,
ret = kstrtoint(buf, 10, &digit);
if (ret)
return ret;
+ down_write(&current_image_rwsem);
if (0 <= digit && digit < SMEM_IMAGE_VERSION_BLOCKS_COUNT)
current_image = digit;
else
current_image = 0;
+ up_write(&current_image_rwsem);
return count;
}
@@ -1235,9 +1265,9 @@ static void * __init setup_dummy_socinfo(void)
dummy_socinfo.id = 327;
strlcpy(dummy_socinfo.build_id, "sda630 - ",
sizeof(dummy_socinfo.build_id));
- } else if (early_machine_is_apq8998()) {
+ } else if (early_machine_is_apq8098()) {
dummy_socinfo.id = 319;
- strlcpy(dummy_socinfo.build_id, "apq8998 - ",
+ strlcpy(dummy_socinfo.build_id, "apq8098 - ",
sizeof(dummy_socinfo.build_id));
}
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 0c44d76bc7c7..10eebb0316e8 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -463,8 +463,7 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event)
switch (event) {
case GLINK_CONNECTED:
pr_debug("GLINK_CONNECTED, ch name [%s].\n", ch->name);
- complete_all(&ch->connect);
-
+ ch->glink_state = event;
/*
* if spcom_notify_state() is called within glink_open()
* then ch->glink_handle is not updated yet.
@@ -484,6 +483,7 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event)
ch->rx_buf_size);
ch->rx_buf_ready = true;
}
+ complete_all(&ch->connect);
break;
case GLINK_LOCAL_DISCONNECTED:
/*
@@ -491,6 +491,7 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event)
* only after *both* sides closed the channel.
*/
pr_debug("GLINK_LOCAL_DISCONNECTED, ch [%s].\n", ch->name);
+ ch->glink_state = event;
complete_all(&ch->disconnect);
break;
case GLINK_REMOTE_DISCONNECTED:
@@ -501,6 +502,8 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event)
*/
pr_err("GLINK_REMOTE_DISCONNECTED, ch [%s].\n", ch->name);
+ ch->glink_state = event;
+
/*
* Abort any blocking read() operation.
* The glink notification might be after REMOTE_DISCONNECT.
@@ -518,8 +521,6 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event)
(int) event, ch->name);
return;
}
-
- ch->glink_state = event;
}
/**
@@ -898,12 +899,12 @@ static int spcom_rx(struct spcom_channel *ch,
goto exit_err;
}
+copy_buf:
if (!ch->glink_rx_buf) {
pr_err("invalid glink_rx_buf.\n");
goto exit_err;
}
-copy_buf:
/* Copy from glink buffer to spcom buffer */
size = min_t(int, ch->actual_rx_size, size);
memcpy(buf, ch->glink_rx_buf, size);
@@ -1723,12 +1724,16 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
pr_debug("ion handle ok.\n");
+ /* ION buf lock doesn't involve any rx/tx data to SP. */
+ mutex_lock(&ch->lock);
+
/* Check if this ION buffer is already locked */
for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
if (ch->ion_handle_table[i] == ion_handle) {
pr_err("fd [%d] ion buf is already locked.\n", fd);
/* decrement back the ref count */
ion_free(spcom_dev->ion_client, ion_handle);
+ mutex_unlock(&ch->lock);
return -EINVAL;
}
}
@@ -1740,6 +1745,7 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
ch->ion_fd_table[i] = fd;
pr_debug("ch [%s] locked ion buf #%d, fd [%d].\n",
ch->name, i, fd);
+ mutex_unlock(&ch->lock);
return 0;
}
}
@@ -1748,6 +1754,8 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
/* decrement back the ref count */
ion_free(spcom_dev->ion_client, ion_handle);
+ mutex_unlock(&ch->lock);
+
return -EFAULT;
}
@@ -1826,8 +1834,13 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch,
return -EINVAL;
}
+ /* ION buf unlock doesn't involve any rx/tx data to SP. */
+ mutex_lock(&ch->lock);
+
ret = spcom_unlock_ion_buf(ch, fd);
+ mutex_unlock(&ch->lock);
+
return ret;
}
@@ -2132,7 +2145,6 @@ static int spcom_device_release(struct inode *inode, struct file *filp)
{
struct spcom_channel *ch;
const char *name = file_to_filename(filp);
- bool connected = false;
pr_debug("Close file [%s].\n", name);
@@ -2154,19 +2166,18 @@ static int spcom_device_release(struct inode *inode, struct file *filp)
}
/* channel might be already closed or disconnected */
- if (spcom_is_channel_open(ch) && spcom_is_channel_connected(ch))
- connected = true;
+ if (!spcom_is_channel_open(ch)) {
+ pr_err("ch [%s] already closed.\n", name);
+ return 0;
+ }
reinit_completion(&ch->disconnect);
spcom_close(ch);
- if (connected) {
- pr_debug("Wait for event GLINK_LOCAL_DISCONNECTED, ch [%s].\n",
- name);
- wait_for_completion(&ch->disconnect);
- pr_debug("GLINK_LOCAL_DISCONNECTED signaled, ch [%s].\n", name);
- }
+ pr_debug("Wait for event GLINK_LOCAL_DISCONNECTED, ch [%s].\n", name);
+ wait_for_completion(&ch->disconnect);
+ pr_debug("GLINK_LOCAL_DISCONNECTED signaled, ch [%s].\n", name);
return 0;
}
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index c35ec26fefa2..d3d0b8594c9f 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -28,7 +28,6 @@
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/idr.h>
-#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
@@ -149,7 +148,6 @@ struct restart_log {
* @restart_level: restart level (0 - panic, 1 - related, 2 - independent, etc.)
* @restart_order: order of other devices this devices restarts with
* @crash_count: number of times the device has crashed
- * @dentry: debugfs directory for this device
* @do_ramdump_on_put: ramdump on subsystem_put() if true
* @err_ready: completion variable to record error ready from subsystem
* @crashed: indicates if subsystem has crashed
@@ -171,9 +169,6 @@ struct subsys_device {
int restart_level;
int crash_count;
struct subsys_soc_restart_order *restart_order;
-#ifdef CONFIG_DEBUG_FS
- struct dentry *dentry;
-#endif
bool do_ramdump_on_put;
struct cdev char_dev;
dev_t dev_no;
@@ -352,10 +347,11 @@ static struct device_attribute subsys_attrs[] = {
__ATTR_NULL,
};
-static struct bus_type subsys_bus_type = {
+struct bus_type subsys_bus_type = {
.name = "msm_subsys",
.dev_attrs = subsys_attrs,
};
+EXPORT_SYMBOL(subsys_bus_type);
static DEFINE_IDA(subsys_ida);
@@ -1169,87 +1165,6 @@ void notify_proxy_unvote(struct device *device)
notify_each_subsys_device(&dev, 1, SUBSYS_PROXY_UNVOTE, NULL);
}
-#ifdef CONFIG_DEBUG_FS
-static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
- size_t cnt, loff_t *ppos)
-{
- int r;
- char buf[40];
- struct subsys_device *subsys = filp->private_data;
-
- r = snprintf(buf, sizeof(buf), "%d\n", subsys->count);
- return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
-}
-
-static ssize_t subsys_debugfs_write(struct file *filp,
- const char __user *ubuf, size_t cnt, loff_t *ppos)
-{
- struct subsys_device *subsys = filp->private_data;
- char buf[10];
- char *cmp;
-
- cnt = min(cnt, sizeof(buf) - 1);
- if (copy_from_user(&buf, ubuf, cnt))
- return -EFAULT;
- buf[cnt] = '\0';
- cmp = strstrip(buf);
-
- if (!strcmp(cmp, "restart")) {
- if (subsystem_restart_dev(subsys))
- return -EIO;
- } else if (!strcmp(cmp, "get")) {
- if (subsystem_get(subsys->desc->name))
- return -EIO;
- } else if (!strcmp(cmp, "put")) {
- subsystem_put(subsys);
- } else {
- return -EINVAL;
- }
-
- return cnt;
-}
-
-static const struct file_operations subsys_debugfs_fops = {
- .open = simple_open,
- .read = subsys_debugfs_read,
- .write = subsys_debugfs_write,
-};
-
-static struct dentry *subsys_base_dir;
-
-static int __init subsys_debugfs_init(void)
-{
- subsys_base_dir = debugfs_create_dir("msm_subsys", NULL);
- return !subsys_base_dir ? -ENOMEM : 0;
-}
-
-static void subsys_debugfs_exit(void)
-{
- debugfs_remove_recursive(subsys_base_dir);
-}
-
-static int subsys_debugfs_add(struct subsys_device *subsys)
-{
- if (!subsys_base_dir)
- return -ENOMEM;
-
- subsys->dentry = debugfs_create_file(subsys->desc->name,
- S_IRUGO | S_IWUSR, subsys_base_dir,
- subsys, &subsys_debugfs_fops);
- return !subsys->dentry ? -ENOMEM : 0;
-}
-
-static void subsys_debugfs_remove(struct subsys_device *subsys)
-{
- debugfs_remove(subsys->dentry);
-}
-#else
-static int __init subsys_debugfs_init(void) { return 0; };
-static void subsys_debugfs_exit(void) { }
-static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; }
-static void subsys_debugfs_remove(struct subsys_device *subsys) { }
-#endif
-
static int subsys_device_open(struct inode *inode, struct file *file)
{
struct subsys_device *device, *subsys_dev = 0;
@@ -1686,17 +1601,8 @@ struct subsys_device *subsys_register(struct subsys_desc *desc)
mutex_init(&subsys->track.lock);
- ret = subsys_debugfs_add(subsys);
- if (ret) {
- ida_simple_remove(&subsys_ida, subsys->id);
- wakeup_source_trash(&subsys->ssr_wlock);
- kfree(subsys);
- return ERR_PTR(ret);
- }
-
ret = device_register(&subsys->dev);
if (ret) {
- subsys_debugfs_remove(subsys);
put_device(&subsys->dev);
return ERR_PTR(ret);
}
@@ -1758,7 +1664,6 @@ err_setup_irqs:
if (ofnode)
subsys_remove_restart_order(ofnode);
err_register:
- subsys_debugfs_remove(subsys);
device_unregister(&subsys->dev);
return ERR_PTR(ret);
}
@@ -1787,7 +1692,6 @@ void subsys_unregister(struct subsys_device *subsys)
WARN_ON(subsys->count);
device_unregister(&subsys->dev);
mutex_unlock(&subsys->track.lock);
- subsys_debugfs_remove(subsys);
subsys_char_device_remove(subsys);
sysmon_notifier_unregister(subsys->desc);
if (subsys->desc->edge)
@@ -1827,9 +1731,6 @@ static int __init subsys_restart_init(void)
ret = bus_register(&subsys_bus_type);
if (ret)
goto err_bus;
- ret = subsys_debugfs_init();
- if (ret)
- goto err_debugfs;
char_class = class_create(THIS_MODULE, "subsys");
if (IS_ERR(char_class)) {
@@ -1848,8 +1749,6 @@ static int __init subsys_restart_init(void)
err_soc:
class_destroy(char_class);
err_class:
- subsys_debugfs_exit();
-err_debugfs:
bus_unregister(&subsys_bus_type);
err_bus:
destroy_workqueue(ssr_wq);
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 470ecfdd9f5e..745a069df88a 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,7 @@
#include <linux/wait.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/memory_dump.h>
+#include <soc/qcom/minidump.h>
#include <soc/qcom/watchdog.h>
#define MODULE_NAME "msm_watchdog"
@@ -521,6 +522,8 @@ void register_scan_dump(struct msm_watchdog_data *wdog_dd)
dump_data->addr = virt_to_phys(dump_addr);
dump_data->len = wdog_dd->scandump_size;
+ strlcpy(dump_data->name, "KSCANDUMP", sizeof(dump_data->name));
+
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);
@@ -605,6 +608,9 @@ static void configure_bark_dump(struct msm_watchdog_data *wdog_dd)
cpu_data[cpu].addr = virt_to_phys(cpu_buf +
cpu * MAX_CPU_CTX_SIZE);
cpu_data[cpu].len = MAX_CPU_CTX_SIZE;
+ snprintf(cpu_data[cpu].name, sizeof(cpu_data[cpu].name),
+ "KCPU_CTX%d", cpu);
+
dump_entry.id = MSM_DUMP_DATA_CPU_CTX + cpu;
dump_entry.addr = virt_to_phys(&cpu_data[cpu]);
ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
@@ -820,6 +826,7 @@ static int msm_watchdog_probe(struct platform_device *pdev)
{
int ret;
struct msm_watchdog_data *wdog_dd;
+ struct md_region md_entry;
if (!pdev->dev.of_node || !enable)
return -ENODEV;
@@ -841,6 +848,15 @@ static int msm_watchdog_probe(struct platform_device *pdev)
goto err;
}
init_watchdog_data(wdog_dd);
+
+ /* Add wdog info to minidump table */
+ strlcpy(md_entry.name, "KWDOGDATA", sizeof(md_entry.name));
+ md_entry.virt_addr = (uintptr_t)wdog_dd;
+ md_entry.phys_addr = virt_to_phys(wdog_dd);
+ md_entry.size = sizeof(*wdog_dd);
+ if (msm_minidump_add_region(&md_entry))
+ pr_info("Failed to add RTB in Minidump\n");
+
return 0;
err:
kzfree(wdog_dd);
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 8efe18dcc98f..548efd11e856 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1496,9 +1496,9 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag)
u32 spi_ioc_orig;
int rc;
- if (dd->suspended) {
- dev_err(dd->dev, "%s: SPI operational state not valid %d\n",
- __func__, dd->suspended);
+ rc = pm_runtime_get_sync(dd->dev);
+ if (rc < 0) {
+ dev_err(dd->dev, "Failure during runtime get");
return;
}
@@ -1524,6 +1524,8 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag)
writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
if (dd->pdata->is_shared)
put_local_resources(dd);
+ pm_runtime_mark_last_busy(dd->dev);
+ pm_runtime_put_autosuspend(dd->dev);
}
static void reset_core(struct msm_spi *dd)
@@ -1707,16 +1709,23 @@ static int msm_spi_prepare_transfer_hardware(struct spi_master *master)
struct msm_spi *dd = spi_master_get_devdata(master);
int resume_state = 0;
- if (!pm_runtime_enabled(dd->dev)) {
- dev_err(dd->dev, "Runtime PM not available\n");
- resume_state = -EBUSY;
- goto spi_finalize;
- }
-
resume_state = pm_runtime_get_sync(dd->dev);
if (resume_state < 0)
goto spi_finalize;
+ /*
+ * Counter-part of system-suspend when runtime-pm is not enabled.
+ * This way, resume can be left empty and device will be put in
+ * active mode only if client requests anything on the bus
+ */
+ if (!pm_runtime_enabled(dd->dev))
+ resume_state = msm_spi_pm_resume_runtime(dd->dev);
+ if (resume_state < 0)
+ goto spi_finalize;
+ if (dd->suspended) {
+ resume_state = -EBUSY;
+ goto spi_finalize;
+ }
return 0;
spi_finalize:
@@ -1728,11 +1737,6 @@ static int msm_spi_unprepare_transfer_hardware(struct spi_master *master)
{
struct msm_spi *dd = spi_master_get_devdata(master);
- if (!pm_runtime_enabled(dd->dev)) {
- dev_err(dd->dev, "Runtime PM not available\n");
- return -EBUSY;
- }
-
pm_runtime_mark_last_busy(dd->dev);
pm_runtime_put_autosuspend(dd->dev);
return 0;
@@ -2672,10 +2676,27 @@ resume_exit:
#ifdef CONFIG_PM_SLEEP
static int msm_spi_suspend(struct device *device)
{
- if (!pm_runtime_status_suspended(device)) {
- dev_err(device, "Runtime not suspended, deny sys suspend");
- return -EBUSY;
+ if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
+ struct platform_device *pdev = to_platform_device(device);
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd;
+
+ dev_dbg(device, "system suspend");
+ if (!master)
+ goto suspend_exit;
+ dd = spi_master_get_devdata(master);
+ if (!dd)
+ goto suspend_exit;
+ msm_spi_pm_suspend_runtime(device);
+
+ /*
+ * set the device's runtime PM status to 'suspended'
+ */
+ pm_runtime_disable(device);
+ pm_runtime_set_suspended(device);
+ pm_runtime_enable(device);
}
+suspend_exit:
return 0;
}
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index a5bfeab596ac..d1802bcba0fb 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -166,6 +166,7 @@ struct spmi_pmic_arb {
u16 max_apid;
u16 max_periph;
u32 *mapping_table;
+ int reserved_chan;
DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
struct irq_domain *domain;
struct spmi_controller *spmic;
@@ -861,6 +862,10 @@ static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid)
* ppid_to_apid is an in-memory invert of that table.
*/
for (apid = pa->last_apid; apid < pa->max_periph; apid++) {
+ /* Do not keep the reserved channel in the mapping table */
+ if (pa->reserved_chan >= 0 && apid == pa->reserved_chan)
+ continue;
+
regval = readl_relaxed(pa->cnfg +
SPMI_OWNERSHIP_TABLE_REG(apid));
pa->apid_data[apid].irq_owner
@@ -920,6 +925,10 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pa)
* receive interrupts from the PPID.
*/
for (apid = 0; apid < pa->max_periph; apid++) {
+ /* Do not keep the reserved channel in the mapping table */
+ if (pa->reserved_chan >= 0 && apid == pa->reserved_chan)
+ continue;
+
offset = pa->ver_ops->channel_map_offset(apid);
if (offset >= pa->core_size)
break;
@@ -1340,6 +1349,10 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
pa->ee = ee;
+ pa->reserved_chan = -EINVAL;
+ of_property_read_u32(pdev->dev.of_node, "qcom,reserved-chan",
+ &pa->reserved_chan);
+
pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1,
sizeof(*pa->mapping_table), GFP_KERNEL);
if (!pa->mapping_table) {
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 3f1133230a1a..28c9afe538ca 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -392,6 +392,7 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
ret = PTR_ERR(vmfile);
goto out;
}
+ vmfile->f_mode |= FMODE_LSEEK;
asma->file = vmfile;
}
get_file(asma->file);
@@ -752,10 +753,12 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
case ASHMEM_SET_SIZE:
ret = -EINVAL;
+ mutex_lock(&ashmem_mutex);
if (!asma->file) {
ret = 0;
asma->size = (size_t)arg;
}
+ mutex_unlock(&ashmem_mutex);
break;
case ASHMEM_GET_SIZE:
ret = asma->size;
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index faa81c28a0d3..58bf3d2f52bd 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -114,6 +114,7 @@ struct ion_client {
*/
struct ion_handle {
struct kref ref;
+ unsigned int user_ref_count;
struct ion_client *client;
struct ion_buffer *buffer;
struct rb_node node;
@@ -433,6 +434,50 @@ int ion_handle_put(struct ion_handle *handle)
return ret;
}
+/* Must hold the client lock */
+static void user_ion_handle_get(struct ion_handle *handle)
+{
+ if (handle->user_ref_count++ == 0)
+ kref_get(&handle->ref);
+}
+
+/* Must hold the client lock */
+static struct ion_handle *user_ion_handle_get_check_overflow(
+ struct ion_handle *handle)
+{
+ if (handle->user_ref_count + 1 == 0)
+ return ERR_PTR(-EOVERFLOW);
+ user_ion_handle_get(handle);
+ return handle;
+}
+
+/* passes a kref to the user ref count.
+ * We know we're holding a kref to the object before and
+ * after this call, so no need to reverify handle.
+ */
+static struct ion_handle *pass_to_user(struct ion_handle *handle)
+{
+ struct ion_client *client = handle->client;
+ struct ion_handle *ret;
+
+ mutex_lock(&client->lock);
+ ret = user_ion_handle_get_check_overflow(handle);
+ ion_handle_put_nolock(handle);
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
+/* Must hold the client lock */
+static int user_ion_handle_put_nolock(struct ion_handle *handle)
+{
+ int ret = 0;
+
+ if (--handle->user_ref_count == 0)
+ ret = ion_handle_put_nolock(handle);
+
+ return ret;
+}
+
static struct ion_handle *ion_handle_lookup(struct ion_client *client,
struct ion_buffer *buffer)
{
@@ -644,6 +689,25 @@ static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle
ion_handle_put_nolock(handle);
}
+static void user_ion_free_nolock(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ bool valid_handle;
+
+ WARN_ON(client != handle->client);
+
+ valid_handle = ion_handle_validate(client, handle);
+ if (!valid_handle) {
+ WARN(1, "%s: invalid handle passed to free.\n", __func__);
+ return;
+ }
+ if (!handle->user_ref_count > 0) {
+ WARN(1, "%s: User does not have access!\n", __func__);
+ return;
+ }
+ user_ion_handle_put_nolock(handle);
+}
+
void ion_free(struct ion_client *client, struct ion_handle *handle)
{
BUG_ON(client != handle->client);
@@ -1518,7 +1582,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
data.allocation.flags, true);
if (IS_ERR(handle))
return PTR_ERR(handle);
-
+ pass_to_user(handle);
data.allocation.handle = handle->id;
cleanup_handle = handle;
@@ -1534,7 +1598,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mutex_unlock(&client->lock);
return PTR_ERR(handle);
}
- ion_free_nolock(client, handle);
+ user_ion_free_nolock(client, handle);
ion_handle_put_nolock(handle);
mutex_unlock(&client->lock);
break;
@@ -1558,10 +1622,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct ion_handle *handle;
handle = ion_import_dma_buf(client, data.fd.fd);
- if (IS_ERR(handle))
+ if (IS_ERR(handle)) {
ret = PTR_ERR(handle);
- else
- data.handle.handle = handle->id;
+ } else {
+ handle = pass_to_user(handle);
+ if (IS_ERR(handle))
+ ret = PTR_ERR(handle);
+ else
+ data.handle.handle = handle->id;
+ }
break;
}
case ION_IOC_SYNC:
@@ -1593,8 +1662,10 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (dir & _IOC_READ) {
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
if (cleanup_handle) {
- ion_free(client, cleanup_handle);
- ion_handle_put(cleanup_handle);
+ mutex_lock(&client->lock);
+ user_ion_free_nolock(client, cleanup_handle);
+ ion_handle_put_nolock(cleanup_handle);
+ mutex_unlock(&client->lock);
}
return -EFAULT;
}
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index bec687853c5d..205af6627b80 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -217,6 +217,22 @@ static int test_task_flag(struct task_struct *p, int flag)
return 0;
}
+static int test_task_state(struct task_struct *p, int state)
+{
+ struct task_struct *t;
+
+ for_each_thread(p, t) {
+ task_lock(t);
+ if (t->state & state) {
+ task_unlock(t);
+ return 1;
+ }
+ task_unlock(t);
+ }
+
+ return 0;
+}
+
static DEFINE_MUTEX(scan_mutex);
int can_use_cma_pages(gfp_t gfp_mask)
@@ -404,7 +420,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
int other_free;
int other_file;
- if (mutex_lock_interruptible(&scan_mutex) < 0)
+ if (!mutex_trylock(&scan_mutex))
return 0;
other_free = global_page_state(NR_FREE_PAGES);
@@ -462,8 +478,6 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {
if (test_task_flag(tsk, TIF_MEMDIE)) {
rcu_read_unlock();
- /* give the system time to free up the memory */
- msleep_interruptible(20);
mutex_unlock(&scan_mutex);
return 0;
}
@@ -497,6 +511,17 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
}
if (selected) {
long cache_size, cache_limit, free;
+
+ if (test_task_flag(selected, TIF_MEMDIE) &&
+ (test_task_state(selected, TASK_UNINTERRUPTIBLE))) {
+ lowmem_print(2, "'%s' (%d) is already killed\n",
+ selected->comm,
+ selected->pid);
+ rcu_read_unlock();
+ mutex_unlock(&scan_mutex);
+ return 0;
+ }
+
task_lock(selected);
send_sig(SIGKILL, selected, 0);
/*
@@ -565,7 +590,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
static struct shrinker lowmem_shrinker = {
.scan_objects = lowmem_scan,
.count_objects = lowmem_count,
- .seeks = DEFAULT_SEEKS * 16
+ .seeks = DEFAULT_SEEKS * 16,
+ .flags = SHRINKER_LMK
};
static int __init lowmem_init(void)
diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c
index 9b8d17ce3a5e..5238d67490ce 100644
--- a/drivers/staging/android/sync.c
+++ b/drivers/staging/android/sync.c
@@ -29,6 +29,7 @@
#include "sync.h"
#define CREATE_TRACE_POINTS
+#define SYNC_DUMP_TIME_LIMIT 7000
#include "trace/sync.h"
static const struct fence_ops android_fence_ops;
@@ -392,7 +393,9 @@ int sync_fence_wait(struct sync_fence *fence, long timeout)
if (timeout) {
pr_info("fence timeout on [%pK] after %dms\n", fence,
jiffies_to_msecs(timeout));
- sync_dump();
+ if (jiffies_to_msecs(timeout) >=
+ SYNC_DUMP_TIME_LIMIT)
+ sync_dump();
}
return -ETIME;
}
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index de18790eb21c..d72a4058fd08 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -154,7 +154,7 @@ static void pscsi_tape_read_blocksize(struct se_device *dev,
buf = kzalloc(12, GFP_KERNEL);
if (!buf)
- return;
+ goto out_free;
memset(cdb, 0, MAX_COMMAND_SIZE);
cdb[0] = MODE_SENSE;
@@ -169,9 +169,10 @@ static void pscsi_tape_read_blocksize(struct se_device *dev,
* If MODE_SENSE still returns zero, set the default value to 1024.
*/
sdev->sector_size = (buf[9] << 16) | (buf[10] << 8) | (buf[11]);
+out_free:
if (!sdev->sector_size)
sdev->sector_size = 1024;
-out_free:
+
kfree(buf);
}
@@ -314,9 +315,10 @@ static int pscsi_add_device_to_list(struct se_device *dev,
sd->lun, sd->queue_depth);
}
- dev->dev_attrib.hw_block_size = sd->sector_size;
+ dev->dev_attrib.hw_block_size =
+ min_not_zero((int)sd->sector_size, 512);
dev->dev_attrib.hw_max_sectors =
- min_t(int, sd->host->max_sectors, queue_max_hw_sectors(q));
+ min_not_zero(sd->host->max_sectors, queue_max_hw_sectors(q));
dev->dev_attrib.hw_queue_depth = sd->queue_depth;
/*
@@ -339,8 +341,10 @@ static int pscsi_add_device_to_list(struct se_device *dev,
/*
* For TYPE_TAPE, attempt to determine blocksize with MODE_SENSE.
*/
- if (sd->type == TYPE_TAPE)
+ if (sd->type == TYPE_TAPE) {
pscsi_tape_read_blocksize(dev, sd);
+ dev->dev_attrib.hw_block_size = sd->sector_size;
+ }
return 0;
}
@@ -406,7 +410,7 @@ static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd)
/*
* Called with struct Scsi_Host->host_lock called.
*/
-static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd)
+static int pscsi_create_type_nondisk(struct se_device *dev, struct scsi_device *sd)
__releases(sh->host_lock)
{
struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
@@ -433,28 +437,6 @@ static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd)
return 0;
}
-/*
- * Called with struct Scsi_Host->host_lock called.
- */
-static int pscsi_create_type_other(struct se_device *dev,
- struct scsi_device *sd)
- __releases(sh->host_lock)
-{
- struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
- struct Scsi_Host *sh = sd->host;
- int ret;
-
- spin_unlock_irq(sh->host_lock);
- ret = pscsi_add_device_to_list(dev, sd);
- if (ret)
- return ret;
-
- pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%llu\n",
- phv->phv_host_id, scsi_device_type(sd->type), sh->host_no,
- sd->channel, sd->id, sd->lun);
- return 0;
-}
-
static int pscsi_configure_device(struct se_device *dev)
{
struct se_hba *hba = dev->se_hba;
@@ -542,11 +524,8 @@ static int pscsi_configure_device(struct se_device *dev)
case TYPE_DISK:
ret = pscsi_create_type_disk(dev, sd);
break;
- case TYPE_ROM:
- ret = pscsi_create_type_rom(dev, sd);
- break;
default:
- ret = pscsi_create_type_other(dev, sd);
+ ret = pscsi_create_type_nondisk(dev, sd);
break;
}
@@ -611,8 +590,7 @@ static void pscsi_free_device(struct se_device *dev)
else if (pdv->pdv_lld_host)
scsi_host_put(pdv->pdv_lld_host);
- if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM))
- scsi_device_put(sd);
+ scsi_device_put(sd);
pdv->pdv_sd = NULL;
}
@@ -1088,7 +1066,6 @@ static sector_t pscsi_get_blocks(struct se_device *dev)
if (pdv->pdv_bd && pdv->pdv_bd->bd_part)
return pdv->pdv_bd->bd_part->nr_sects;
- dump_stack();
return 0;
}
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 2e27b1034ede..90c5dffc9fa4 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -1096,9 +1096,15 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
return ret;
break;
case VERIFY:
+ case VERIFY_16:
size = 0;
- sectors = transport_get_sectors_10(cdb);
- cmd->t_task_lba = transport_lba_32(cdb);
+ if (cdb[0] == VERIFY) {
+ sectors = transport_get_sectors_10(cdb);
+ cmd->t_task_lba = transport_lba_32(cdb);
+ } else {
+ sectors = transport_get_sectors_16(cdb);
+ cmd->t_task_lba = transport_lba_64(cdb);
+ }
cmd->execute_cmd = sbc_emulate_noop;
goto check_lba;
case REZERO_UNIT:
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 5b24ffd93649..83ff1724ec79 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -57,6 +57,7 @@ struct serial_private {
unsigned int nr;
void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES];
struct pci_serial_quirk *quirk;
+ const struct pciserial_board *board;
int line[0];
};
@@ -4058,6 +4059,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
}
}
priv->nr = i;
+ priv->board = board;
return priv;
err_deinit:
@@ -4068,7 +4070,7 @@ err_out:
}
EXPORT_SYMBOL_GPL(pciserial_init_ports);
-void pciserial_remove_ports(struct serial_private *priv)
+void pciserial_detach_ports(struct serial_private *priv)
{
struct pci_serial_quirk *quirk;
int i;
@@ -4088,7 +4090,11 @@ void pciserial_remove_ports(struct serial_private *priv)
quirk = find_quirk(priv->dev);
if (quirk->exit)
quirk->exit(priv->dev);
+}
+void pciserial_remove_ports(struct serial_private *priv)
+{
+ pciserial_detach_ports(priv);
kfree(priv);
}
EXPORT_SYMBOL_GPL(pciserial_remove_ports);
@@ -5819,7 +5825,7 @@ static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev,
return PCI_ERS_RESULT_DISCONNECT;
if (priv)
- pciserial_suspend_ports(priv);
+ pciserial_detach_ports(priv);
pci_disable_device(dev);
@@ -5844,9 +5850,18 @@ static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev)
static void serial8250_io_resume(struct pci_dev *dev)
{
struct serial_private *priv = pci_get_drvdata(dev);
+ const struct pciserial_board *board;
- if (priv)
- pciserial_resume_ports(priv);
+ if (!priv)
+ return;
+
+ board = priv->board;
+ kfree(priv);
+ priv = pciserial_init_ports(dev, board);
+
+ if (!IS_ERR(priv)) {
+ pci_set_drvdata(dev, priv);
+ }
}
static const struct pci_error_handlers serial8250_err_handler = {
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index deaddb950c20..24337ac3323f 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -1105,7 +1105,7 @@ static int usbtmc_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "%s called\n", __func__);
- data = kmalloc(sizeof(*data), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -1163,6 +1163,12 @@ static int usbtmc_probe(struct usb_interface *intf,
}
}
+ if (!data->bulk_out || !data->bulk_in) {
+ dev_err(&intf->dev, "bulk endpoints not found\n");
+ retcode = -ENODEV;
+ goto err_put;
+ }
+
retcode = get_capabilities(data);
if (retcode)
dev_err(&intf->dev, "can't read capabilities\n");
@@ -1186,6 +1192,7 @@ static int usbtmc_probe(struct usb_interface *intf,
error_register:
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+err_put:
kref_put(&data->kref, usbtmc_delete);
return retcode;
}
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index ac30a051ad71..325cbc9c35d8 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -246,6 +246,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
/*
* Adjust bInterval for quirked devices.
+ */
+ /*
+ * This quirk fixes bIntervals reported in ms.
+ */
+ if (to_usb_device(ddev)->quirks &
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL) {
+ n = clamp(fls(d->bInterval) + 3, i, j);
+ i = j = n;
+ }
+ /*
* This quirk fixes bIntervals reported in
* linear microframes.
*/
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 9a3bf5e2977f..755dbca78646 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -966,7 +966,7 @@ static void usb_bus_init (struct usb_bus *bus)
bus->bandwidth_allocated = 0;
bus->bandwidth_int_reqs = 0;
bus->bandwidth_isoc_reqs = 0;
- mutex_init(&bus->usb_address0_mutex);
+ mutex_init(&bus->devnum_next_mutex);
INIT_LIST_HEAD (&bus->bus_list);
}
@@ -2555,6 +2555,14 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
return NULL;
}
if (primary_hcd == NULL) {
+ hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
+ GFP_KERNEL);
+ if (!hcd->address0_mutex) {
+ kfree(hcd);
+ dev_dbg(dev, "hcd address0 mutex alloc failed\n");
+ return NULL;
+ }
+ mutex_init(hcd->address0_mutex);
hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
GFP_KERNEL);
if (!hcd->bandwidth_mutex) {
@@ -2566,6 +2574,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
dev_set_drvdata(dev, hcd);
} else {
mutex_lock(&usb_port_peer_mutex);
+ hcd->address0_mutex = primary_hcd->address0_mutex;
hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
hcd->primary_hcd = primary_hcd;
primary_hcd->primary_hcd = primary_hcd;
@@ -2622,24 +2631,23 @@ EXPORT_SYMBOL_GPL(usb_create_hcd);
* Don't deallocate the bandwidth_mutex until the last shared usb_hcd is
* deallocated.
*
- * Make sure to only deallocate the bandwidth_mutex when the primary HCD is
- * freed. When hcd_release() is called for either hcd in a peer set
- * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to
- * block new peering attempts
+ * Make sure to deallocate the bandwidth_mutex only when the last HCD is
+ * freed. When hcd_release() is called for either hcd in a peer set,
+ * invalidate the peer's ->shared_hcd and ->primary_hcd pointers.
*/
static void hcd_release(struct kref *kref)
{
struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
mutex_lock(&usb_port_peer_mutex);
- if (hcd->primary_hcd == hcd)
- kfree(hcd->bandwidth_mutex);
if (hcd->shared_hcd) {
struct usb_hcd *peer = hcd->shared_hcd;
peer->shared_hcd = NULL;
- if (peer->primary_hcd == hcd)
- peer->primary_hcd = NULL;
+ peer->primary_hcd = NULL;
+ } else {
+ kfree(hcd->address0_mutex);
+ kfree(hcd->bandwidth_mutex);
}
mutex_unlock(&usb_port_peer_mutex);
kfree(hcd);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index f84ef04284f5..41b1ef2135d7 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1991,7 +1991,7 @@ static void choose_devnum(struct usb_device *udev)
struct usb_bus *bus = udev->bus;
/* be safe when more hub events are proceed in parallel */
- mutex_lock(&bus->usb_address0_mutex);
+ mutex_lock(&bus->devnum_next_mutex);
if (udev->wusb) {
devnum = udev->portnum + 1;
BUG_ON(test_bit(devnum, bus->devmap.devicemap));
@@ -2009,7 +2009,7 @@ static void choose_devnum(struct usb_device *udev)
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
- mutex_unlock(&bus->usb_address0_mutex);
+ mutex_unlock(&bus->devnum_next_mutex);
}
static void release_devnum(struct usb_device *udev)
@@ -4212,7 +4212,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
int connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
- if (!udev->usb2_hw_lpm_capable)
+ if (!udev->usb2_hw_lpm_capable || !udev->bos)
return;
if (hub)
@@ -4275,7 +4275,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
if (oldspeed == USB_SPEED_LOW)
delay = HUB_LONG_RESET_TIME;
- mutex_lock(&hdev->bus->usb_address0_mutex);
+ mutex_lock(hcd->address0_mutex);
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
@@ -4561,7 +4561,7 @@ fail:
hub_port_disable(hub, port1, 0);
update_devnum(udev, devnum); /* for disconnect processing */
}
- mutex_unlock(&hdev->bus->usb_address0_mutex);
+ mutex_unlock(hcd->address0_mutex);
return retval;
}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 24f9f98968a5..96b21b0dac1e 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -170,6 +170,14 @@ static const struct usb_device_id usb_quirk_list[] = {
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Baum Vario Ultra */
+ { USB_DEVICE(0x0904, 0x6101), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ { USB_DEVICE(0x0904, 0x6102), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ { USB_DEVICE(0x0904, 0x6103), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+
/* Keytouch QWERTY Panel keyboard */
{ USB_DEVICE(0x0926, 0x3333), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index a80fb34cdce8..e394f123f918 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -764,6 +764,7 @@ static int dwc3_msm_ep_queue(struct usb_ep *ep,
return 0;
err:
+ list_del(&req_complete->list_item);
spin_unlock_irqrestore(&dwc->lock, flags);
kfree(req_complete);
return ret;
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index c31aaf7a9880..e5d3a0bdf32a 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -160,7 +160,7 @@ static int usb_string_copy(const char *s, char **s_copy)
if (!str)
return -ENOMEM;
}
- strncpy(str, s, MAX_USB_STRING_WITH_NULL_LEN);
+ strlcpy(str, s, MAX_USB_STRING_WITH_NULL_LEN);
if (str[ret - 1] == '\n')
str[ret - 1] = '\0';
*s_copy = str;
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index cd096fb9078f..1ef3442cf618 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -77,9 +77,13 @@ struct acc_dev {
struct usb_ep *ep_in;
struct usb_ep *ep_out;
- /* set to 1 when we connect */
+ /* online indicates state of function_set_alt & function_unbind
+ * set to 1 when we connect
+ */
int online:1;
- /* Set to 1 when we disconnect.
+
+ /* disconnected indicates state of open & release
+ * Set to 1 when we disconnect.
* Not cleared until our file is closed.
*/
int disconnected:1;
@@ -307,7 +311,6 @@ static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head)
static void acc_set_disconnected(struct acc_dev *dev)
{
- dev->online = 0;
dev->disconnected = 1;
}
@@ -721,9 +724,10 @@ static ssize_t acc_write(struct file *fp, const char __user *buf,
req->zero = 0;
} else {
xfer = count;
- /* If the data length is a multple of the
+ /*
+ * If the data length is a multple of the
* maxpacket size then send a zero length packet(ZLP).
- */
+ */
req->zero = ((xfer % dev->ep_in->maxpacket) == 0);
}
if (copy_from_user(req->buf, buf, xfer)) {
@@ -808,7 +812,10 @@ static int acc_release(struct inode *ip, struct file *fp)
printk(KERN_INFO "acc_release\n");
WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0));
- _acc_dev->disconnected = 0;
+ /* indicate that we are disconnected
+ * still could be online so don't touch online flag
+ */
+ _acc_dev->disconnected = 1;
return 0;
}
@@ -868,11 +875,11 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev,
unsigned long flags;
/*
- printk(KERN_INFO "acc_ctrlrequest "
- "%02x.%02x v%04x i%04x l%u\n",
- b_requestType, b_request,
- w_value, w_index, w_length);
-*/
+ * printk(KERN_INFO "acc_ctrlrequest "
+ * "%02x.%02x v%04x i%04x l%u\n",
+ * b_requestType, b_request,
+ * w_value, w_index, w_length);
+ */
if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) {
if (b_request == ACCESSORY_START) {
@@ -1069,6 +1076,10 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
struct usb_request *req;
int i;
+ dev->online = 0; /* clear online flag */
+ wake_up(&dev->read_wq); /* unblock reads on closure */
+ wake_up(&dev->write_wq); /* likewise for writes */
+
while ((req = req_get(dev, &dev->tx_idle)))
acc_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
@@ -1200,6 +1211,7 @@ static int acc_function_set_alt(struct usb_function *f,
}
dev->online = 1;
+ dev->disconnected = 0; /* if online then not disconnected */
/* readers may be blocked waiting for us to go online */
wake_up(&dev->read_wq);
@@ -1212,7 +1224,8 @@ static void acc_function_disable(struct usb_function *f)
struct usb_composite_dev *cdev = dev->cdev;
DBG(cdev, "acc_function_disable\n");
- acc_set_disconnected(dev);
+ acc_set_disconnected(dev); /* this now only sets disconnected */
+ dev->online = 0; /* so now need to clear online flag here too */
usb_ep_disable(dev->ep_in);
usb_ep_disable(dev->ep_out);
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index 651e4afe0520..1bcfe819fad3 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -535,13 +535,15 @@ static int acm_notify_serial_state(struct f_acm *acm)
{
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
int status;
+ __le16 serial_state;
spin_lock(&acm->lock);
if (acm->notify_req) {
dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n",
acm->port_num, acm->serial_state);
+ serial_state = cpu_to_le16(acm->serial_state);
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
- 0, &acm->serial_state, sizeof(acm->serial_state));
+ 0, &serial_state, sizeof(acm->serial_state));
} else {
acm->pending = true;
status = 0;
diff --git a/drivers/usb/gadget/function/f_ccid.h b/drivers/usb/gadget/function/f_ccid.h
index 42a7ebbbccfc..935308cff0bc 100644
--- a/drivers/usb/gadget/function/f_ccid.h
+++ b/drivers/usb/gadget/function/f_ccid.h
@@ -55,29 +55,29 @@
#define CCID_READ_DTR _IOR('C', 3, int)
struct usb_ccid_notification {
- unsigned char buf[4];
+ __u8 buf[4];
} __packed;
struct ccid_bulk_in_header {
- unsigned char bMessageType;
- unsigned long wLength;
- unsigned char bSlot;
- unsigned char bSeq;
- unsigned char bStatus;
- unsigned char bError;
- unsigned char bSpecific;
- unsigned char abData[ABDATA_SIZE];
- unsigned char bSizeToSend;
+ __u8 bMessageType;
+ __u32 wLength;
+ __u8 bSlot;
+ __u8 bSeq;
+ __u8 bStatus;
+ __u8 bError;
+ __u8 bSpecific;
+ __u8 abData[ABDATA_SIZE];
+ __u8 bSizeToSend;
} __packed;
struct ccid_bulk_out_header {
- unsigned char bMessageType;
- unsigned long wLength;
- unsigned char bSlot;
- unsigned char bSeq;
- unsigned char bSpecific_0;
- unsigned char bSpecific_1;
- unsigned char bSpecific_2;
- unsigned char APDU[ABDATA_SIZE];
+ __u8 bMessageType;
+ __u32 wLength;
+ __u8 bSlot;
+ __u8 bSeq;
+ __u8 bSpecific_0;
+ __u8 bSpecific_1;
+ __u8 bSpecific_2;
+ __u8 APDU[ABDATA_SIZE];
} __packed;
#endif
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index a1c00525a598..7c35241a487a 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -71,6 +71,7 @@ __ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);
/* The function structure ***************************************************/
struct ffs_ep;
+static bool first_read_done;
struct ffs_function {
struct usb_configuration *conf;
@@ -756,6 +757,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
{
struct ffs_epfile *epfile = file->private_data;
struct ffs_ep *ep;
+ struct ffs_data *ffs = epfile->ffs;
char *data = NULL;
ssize_t ret, data_len = -EINVAL;
int halt;
@@ -764,6 +766,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
atomic_read(&epfile->error), io_data->read ? "READ" : "WRITE");
smp_mb__before_atomic();
+retry:
if (atomic_read(&epfile->error))
return -ENODEV;
@@ -968,10 +971,36 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
* disabled (disconnect) or changed
* (composition switch) ?
*/
- if (epfile->ep == ep)
+ if (epfile->ep == ep) {
ret = ep->status;
- else
+ if (ret >= 0)
+ first_read_done = true;
+ } else {
ret = -ENODEV;
+ }
+
+ /* do wait again if func eps are not enabled */
+ if (io_data->read && !first_read_done
+ && ret < 0) {
+ unsigned short count = ffs->eps_count;
+
+ pr_debug("%s: waiting for the online state\n",
+ __func__);
+ ret = 0;
+ kfree(data);
+ data = NULL;
+ data_len = -EINVAL;
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ mutex_unlock(&epfile->mutex);
+ epfile = ffs->epfiles;
+ do {
+ atomic_set(&epfile->error, 0);
+ ++epfile;
+ } while (--count);
+ epfile = file->private_data;
+ goto retry;
+ }
+
spin_unlock_irq(&epfile->ffs->eps_lock);
if (io_data->read && ret > 0) {
@@ -1038,6 +1067,7 @@ ffs_epfile_open(struct inode *inode, struct file *file)
smp_mb__before_atomic();
atomic_set(&epfile->error, 0);
+ first_read_done = false;
ffs_log("exit:state %d setup_state %d flag %lu", epfile->ffs->state,
epfile->ffs->setup_state, epfile->ffs->flags);
@@ -1159,7 +1189,7 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
*to = p->data;
}
- ffs_log("enter");
+ ffs_log("exit");
return res;
}
@@ -3354,6 +3384,8 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->func) {
ffs_func_eps_disable(ffs->func);
ffs->func = NULL;
+ /* matching put to allow LPM on disconnect */
+ usb_gadget_autopm_put_async(ffs->gadget);
}
if (ffs->state == FFS_DEACTIVATED) {
@@ -3387,14 +3419,9 @@ static int ffs_func_set_alt(struct usb_function *f,
static void ffs_func_disable(struct usb_function *f)
{
- struct ffs_function *func = ffs_func_from_usb(f);
- struct ffs_data *ffs = func->ffs;
-
ffs_log("enter");
ffs_func_set_alt(f, 0, (unsigned)-1);
- /* matching put to allow LPM on disconnect */
- usb_gadget_autopm_put_async(ffs->gadget);
ffs_log("exit");
}
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 29b41b5dee04..c7689d05356c 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -625,7 +625,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
uvc_ss_streaming_comp.wBytesPerInterval =
cpu_to_le16(max_packet_size * max_packet_mult *
- opts->streaming_maxburst);
+ (opts->streaming_maxburst + 1));
/* Allocate endpoints. */
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index 4e38683c653c..6d4e75785710 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -346,6 +346,9 @@ static int idmouse_probe(struct usb_interface *interface,
if (iface_desc->desc.bInterfaceClass != 0x0A)
return -ENODEV;
+ if (iface_desc->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL)
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 86b4e4b2ab9a..383fa007348f 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -370,6 +370,10 @@ static int lvs_rh_probe(struct usb_interface *intf,
hdev = interface_to_usbdev(intf);
desc = intf->cur_altsetting;
+
+ if (desc->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
endpoint = &desc->endpoint[0].desc;
/* valid only for SS root hub */
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index bbd029c9c725..442b6631162e 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -711,6 +711,11 @@ static int uss720_probe(struct usb_interface *intf,
interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints < 3) {
+ usb_put_dev(usbdev);
+ return -ENODEV;
+ }
+
/*
* Allocate parport interface
*/
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index e499b862a946..88f26ac2a185 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -250,8 +250,27 @@ static void cppi41_dma_callback(void *private_data)
transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0;
- if (cppi41_channel->is_tx)
- empty = musb_is_tx_fifo_empty(hw_ep);
+ if (cppi41_channel->is_tx) {
+ u8 type;
+
+ if (is_host_active(musb))
+ type = hw_ep->out_qh->type;
+ else
+ type = hw_ep->ep_in.type;
+
+ if (type == USB_ENDPOINT_XFER_ISOC)
+ /*
+ * Don't use the early-TX-interrupt workaround below
+ * for Isoch transfter. Since Isoch are periodic
+ * transfer, by the time the next transfer is
+ * scheduled, the current one should be done already.
+ *
+ * This avoids audio playback underrun issue.
+ */
+ empty = true;
+ else
+ empty = musb_is_tx_fifo_empty(hw_ep);
+ }
if (!cppi41_channel->is_tx || empty) {
cppi41_trans_done(cppi41_channel);
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index aa11cf2f7417..6ba742076baa 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -222,11 +222,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
"enable phy->fpc_redrive_ldo failed\n");
return rc;
}
-
- dev_dbg(phy->phy.dev,
- "fpc redrive ldo: min_vol:%duV max_vol:%duV\n",
- phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN],
- phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]);
}
rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels,
@@ -236,11 +231,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
goto disable_fpc_redrive;
}
- dev_dbg(phy->phy.dev,
- "vdd ldo: min_vol:%duV max_vol:%duV\n",
- phy->vdd_levels[VOLTAGE_LEVEL_MIN],
- phy->vdd_levels[VOLTAGE_LEVEL_MAX]);
-
rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels,
USB_SSPHY_HPM_LOAD);
if (rc < 0) {
@@ -248,11 +238,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
goto disable_vdd;
}
- dev_dbg(phy->phy.dev,
- "core ldo: min_vol:%duV max_vol:%duV\n",
- phy->core_voltage_levels[VOLTAGE_LEVEL_MIN],
- phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]);
-
return 0;
disable_regulators:
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 42cc72e54c05..af67a0de6b5d 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -233,6 +233,14 @@ static void option_instat_callback(struct urb *urb);
#define BANDRICH_PRODUCT_1012 0x1012
#define QUALCOMM_VENDOR_ID 0x05C6
+/* These Quectel products use Qualcomm's vendor ID */
+#define QUECTEL_PRODUCT_UC20 0x9003
+#define QUECTEL_PRODUCT_UC15 0x9090
+
+#define QUECTEL_VENDOR_ID 0x2c7c
+/* These Quectel products use Quectel's vendor ID */
+#define QUECTEL_PRODUCT_EC21 0x0121
+#define QUECTEL_PRODUCT_EC25 0x0125
#define CMOTECH_VENDOR_ID 0x16d8
#define CMOTECH_PRODUCT_6001 0x6001
@@ -1161,7 +1169,14 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */
- { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9003), /* Quectel UC20 */
+ /* Quectel products using Qualcomm vendor ID */
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ /* Quectel products using Quectel vendor ID */
+ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 696458db7e3c..38b3f0d8cd58 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -169,6 +169,8 @@ static const struct usb_device_id id_table[] = {
{DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81b1)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
+ {DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */
+ {DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */
/* Huawei devices */
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
index 252c7bd9218a..d01496fd27fe 100644
--- a/drivers/usb/wusbcore/wa-hc.c
+++ b/drivers/usb/wusbcore/wa-hc.c
@@ -39,6 +39,9 @@ int wa_create(struct wahc *wa, struct usb_interface *iface,
int result;
struct device *dev = &iface->dev;
+ if (iface->cur_altsetting->desc.bNumEndpoints < 3)
+ return -ENODEV;
+
result = wa_rpipes_create(wa);
if (result < 0)
goto error_rpipes_create;
diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c
index 0257f35cfb9d..e75bbe5a10cd 100644
--- a/drivers/uwb/hwa-rc.c
+++ b/drivers/uwb/hwa-rc.c
@@ -825,6 +825,9 @@ static int hwarc_probe(struct usb_interface *iface,
struct hwarc *hwarc;
struct device *dev = &iface->dev;
+ if (iface->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
result = -ENOMEM;
uwb_rc = uwb_rc_alloc();
if (uwb_rc == NULL) {
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c
index 2bfc846ac071..6345e85822a4 100644
--- a/drivers/uwb/i1480/dfu/usb.c
+++ b/drivers/uwb/i1480/dfu/usb.c
@@ -362,6 +362,9 @@ int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
result);
}
+ if (iface->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
result = -ENOMEM;
i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL);
if (i1480_usb == NULL) {
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 0582b72ef377..1a9f18b40be6 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -511,6 +511,12 @@ static long tce_iommu_build_v2(struct tce_container *container,
unsigned long hpa;
enum dma_data_direction dirtmp;
+ if (!tbl->it_userspace) {
+ ret = tce_iommu_userspace_view_alloc(tbl);
+ if (ret)
+ return ret;
+ }
+
for (i = 0; i < pages; ++i) {
struct mm_iommu_table_group_mem_t *mem = NULL;
unsigned long *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl,
@@ -584,15 +590,6 @@ static long tce_iommu_create_table(struct tce_container *container,
WARN_ON(!ret && !(*ptbl)->it_ops->free);
WARN_ON(!ret && ((*ptbl)->it_allocated_size != table_size));
- if (!ret && container->v2) {
- ret = tce_iommu_userspace_view_alloc(*ptbl);
- if (ret)
- (*ptbl)->it_ops->free(*ptbl);
- }
-
- if (ret)
- decrement_locked_vm(table_size >> PAGE_SHIFT);
-
return ret;
}
@@ -1064,10 +1061,7 @@ static int tce_iommu_take_ownership(struct tce_container *container,
if (!tbl || !tbl->it_map)
continue;
- rc = tce_iommu_userspace_view_alloc(tbl);
- if (!rc)
- rc = iommu_take_ownership(tbl);
-
+ rc = iommu_take_ownership(tbl);
if (rc) {
for (j = 0; j < i; ++j)
iommu_release_ownership(
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 6e92917ba77a..4e3c78d88832 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -1168,6 +1168,8 @@ static void fbcon_free_font(struct display *p, bool freefont)
p->userfont = 0;
}
+static void set_vc_hi_font(struct vc_data *vc, bool set);
+
static void fbcon_deinit(struct vc_data *vc)
{
struct display *p = &fb_display[vc->vc_num];
@@ -1203,6 +1205,9 @@ finished:
if (free_font)
vc->vc_font.data = NULL;
+ if (vc->vc_hi_font_mask)
+ set_vc_hi_font(vc, false);
+
if (!con_is_bound(&fb_con))
fbcon_exit();
@@ -2439,32 +2444,10 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
return 0;
}
-static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
- const u8 * data, int userfont)
+/* set/clear vc_hi_font_mask and update vc attrs accordingly */
+static void set_vc_hi_font(struct vc_data *vc, bool set)
{
- struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
- struct fbcon_ops *ops = info->fbcon_par;
- struct display *p = &fb_display[vc->vc_num];
- int resize;
- int cnt;
- char *old_data = NULL;
-
- if (CON_IS_VISIBLE(vc) && softback_lines)
- fbcon_set_origin(vc);
-
- resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
- if (p->userfont)
- old_data = vc->vc_font.data;
- if (userfont)
- cnt = FNTCHARCNT(data);
- else
- cnt = 256;
- vc->vc_font.data = (void *)(p->fontdata = data);
- if ((p->userfont = userfont))
- REFCOUNT(data)++;
- vc->vc_font.width = w;
- vc->vc_font.height = h;
- if (vc->vc_hi_font_mask && cnt == 256) {
+ if (!set) {
vc->vc_hi_font_mask = 0;
if (vc->vc_can_do_color) {
vc->vc_complement_mask >>= 1;
@@ -2487,7 +2470,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
((c & 0xfe00) >> 1) | (c & 0xff);
vc->vc_attr >>= 1;
}
- } else if (!vc->vc_hi_font_mask && cnt == 512) {
+ } else {
vc->vc_hi_font_mask = 0x100;
if (vc->vc_can_do_color) {
vc->vc_complement_mask <<= 1;
@@ -2519,8 +2502,38 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
} else
vc->vc_video_erase_char = c & ~0x100;
}
-
}
+}
+
+static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
+ const u8 * data, int userfont)
+{
+ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
+ struct fbcon_ops *ops = info->fbcon_par;
+ struct display *p = &fb_display[vc->vc_num];
+ int resize;
+ int cnt;
+ char *old_data = NULL;
+
+ if (CON_IS_VISIBLE(vc) && softback_lines)
+ fbcon_set_origin(vc);
+
+ resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
+ if (p->userfont)
+ old_data = vc->vc_font.data;
+ if (userfont)
+ cnt = FNTCHARCNT(data);
+ else
+ cnt = 256;
+ vc->vc_font.data = (void *)(p->fontdata = data);
+ if ((p->userfont = userfont))
+ REFCOUNT(data)++;
+ vc->vc_font.width = w;
+ vc->vc_font.height = h;
+ if (vc->vc_hi_font_mask && cnt == 256)
+ set_vc_hi_font(vc, false);
+ else if (!vc->vc_hi_font_mask && cnt == 512)
+ set_vc_hi_font(vc, true);
if (resize) {
int cols, rows;
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 17644e3556b6..e9ba77501b38 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -226,6 +226,7 @@ static struct mdp_input_layer *__create_layer_list(
layer->transp_mask = layer32->transp_mask;
layer->bg_color = layer32->bg_color;
layer->blend_op = layer32->blend_op;
+ layer->alpha = layer32->alpha;
layer->color_space = layer32->color_space;
layer->src_rect = layer32->src_rect;
layer->dst_rect = layer32->dst_rect;
diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c
index 0808a1a6e14b..3330f8f62b78 100644
--- a/drivers/video/fbdev/msm/mdss_dba_utils.c
+++ b/drivers/video/fbdev/msm/mdss_dba_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,6 +46,7 @@ struct mdss_dba_utils_data {
struct cec_cbs ccbs;
char disp_switch_name[MAX_SWITCH_NAME_SIZE];
u32 current_vic;
+ bool support_audio;
};
static struct mdss_dba_utils_data *mdss_dba_utils_get_data(
@@ -84,7 +85,7 @@ end:
return udata;
}
-static void mdss_dba_utils_send_display_notification(
+static void mdss_dba_utils_notify_display(
struct mdss_dba_utils_data *udata, int val)
{
int state = 0;
@@ -109,7 +110,7 @@ static void mdss_dba_utils_send_display_notification(
udata->sdev_display.state);
}
-static void mdss_dba_utils_send_audio_notification(
+static void mdss_dba_utils_notify_audio(
struct mdss_dba_utils_data *udata, int val)
{
int state = 0;
@@ -291,6 +292,31 @@ static void mdss_dba_utils_sysfs_remove(struct kobject *kobj)
sysfs_remove_group(kobj, &mdss_dba_utils_fs_attrs_group);
}
+static bool mdss_dba_check_audio_support(struct mdss_dba_utils_data *udata)
+{
+ bool dvi_mode = false;
+ int audio_blk_size = 0;
+ struct msm_ext_disp_audio_edid_blk audio_blk;
+
+ if (!udata) {
+ pr_debug("%s: Invalid input\n", __func__);
+ return false;
+ }
+ memset(&audio_blk, 0, sizeof(audio_blk));
+
+ /* check if sink is in DVI mode */
+ dvi_mode = hdmi_edid_is_dvi_mode(udata->edid_data);
+
+ /* get the audio block size info from EDID */
+ hdmi_edid_get_audio_blk(udata->edid_data, &audio_blk);
+ audio_blk_size = audio_blk.audio_data_blk_size;
+
+ if (dvi_mode || !audio_blk_size)
+ return false;
+ else
+ return true;
+}
+
static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
{
int ret = -EINVAL;
@@ -322,19 +348,26 @@ static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
if (!ret) {
hdmi_edid_parser(udata->edid_data);
- hdmi_edid_get_audio_blk(udata->edid_data, &blk);
- if (udata->ops.set_audio_block)
- udata->ops.set_audio_block(
+ /* check whether audio is supported or not */
+ udata->support_audio =
+ mdss_dba_check_audio_support(udata);
+ if (udata->support_audio) {
+ hdmi_edid_get_audio_blk(
+ udata->edid_data, &blk);
+ if (udata->ops.set_audio_block)
+ udata->ops.set_audio_block(
udata->dba_data,
sizeof(blk), &blk);
+ }
} else {
pr_err("failed to get edid%d\n", ret);
}
}
if (pluggable) {
- mdss_dba_utils_send_display_notification(udata, 1);
- mdss_dba_utils_send_audio_notification(udata, 1);
+ mdss_dba_utils_notify_display(udata, 1);
+ if (udata->support_audio)
+ mdss_dba_utils_notify_audio(udata, 1);
} else {
mdss_dba_utils_video_on(udata, udata->pinfo);
}
@@ -346,8 +379,9 @@ static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
if (!udata->hpd_state)
break;
if (pluggable) {
- mdss_dba_utils_send_audio_notification(udata, 0);
- mdss_dba_utils_send_display_notification(udata, 0);
+ if (udata->support_audio)
+ mdss_dba_utils_notify_audio(udata, 0);
+ mdss_dba_utils_notify_display(udata, 0);
} else {
mdss_dba_utils_video_off(udata);
}
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index e60869144339..ddb7a4c31f68 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -44,6 +44,42 @@
#define INVALID_XIN_ID 0xFF
+static u32 dsi_dbg_bus_sdm660[] = {
+ 0x0001, 0x1001, 0x0001, 0x0011,
+ 0x1021, 0x0021, 0x0031, 0x0041,
+ 0x0051, 0x0061, 0x3061, 0x0061,
+ 0x2061, 0x2061, 0x1061, 0x1061,
+ 0x1061, 0x0071, 0x0071, 0x0071,
+ 0x0081, 0x0081, 0x00A1, 0x00A1,
+ 0x10A1, 0x20A1, 0x30A1, 0x10A1,
+ 0x10A1, 0x30A1, 0x20A1, 0x00B1,
+ 0x00C1, 0x00C1, 0x10C1, 0x20C1,
+ 0x30C1, 0x00D1, 0x00D1, 0x20D1,
+ 0x30D1, 0x00E1, 0x00E1, 0x00E1,
+ 0x00F1, 0x00F1, 0x0101, 0x0101,
+ 0x1101, 0x2101, 0x3101, 0x0111,
+ 0x0141, 0x1141, 0x0141, 0x1141,
+ 0x1141, 0x0151, 0x0151, 0x1151,
+ 0x2151, 0x3151, 0x0161, 0x0161,
+ 0x1161, 0x0171, 0x0171, 0x0181,
+ 0x0181, 0x0191, 0x0191, 0x01A1,
+ 0x01A1, 0x01B1, 0x01B1, 0x11B1,
+ 0x21B1, 0x01C1, 0x01C1, 0x11C1,
+ 0x21C1, 0x31C1, 0x01D1, 0x01D1,
+ 0x01D1, 0x01D1, 0x11D1, 0x21D1,
+ 0x21D1, 0x01E1, 0x01E1, 0x01F1,
+ 0x01F1, 0x0201, 0x0201, 0x0211,
+ 0x0221, 0x0231, 0x0241, 0x0251,
+ 0x0281, 0x0291, 0x0281, 0x0291,
+ 0x02A1, 0x02B1, 0x02C1, 0x0321,
+ 0x0321, 0x1321, 0x2321, 0x3321,
+ 0x0331, 0x0331, 0x1331, 0x0341,
+ 0x0341, 0x1341, 0x2341, 0x3341,
+ 0x0351, 0x0361, 0x0361, 0x1361,
+ 0x2361, 0x0371, 0x0381, 0x0391,
+ 0x03C1, 0x03D1, 0x03E1, 0x03F1,
+};
+
static DEFINE_MUTEX(mdss_debug_lock);
static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};
@@ -59,11 +95,13 @@ static int panel_debug_base_open(struct inode *inode, struct file *file)
static int panel_debug_base_release(struct inode *inode, struct file *file)
{
struct mdss_debug_base *dbg = file->private_data;
+ mutex_lock(&mdss_debug_lock);
if (dbg && dbg->buf) {
kfree(dbg->buf);
dbg->buf_len = 0;
dbg->buf = NULL;
}
+ mutex_unlock(&mdss_debug_lock);
return 0;
}
@@ -385,11 +423,13 @@ static int mdss_debug_base_open(struct inode *inode, struct file *file)
static int mdss_debug_base_release(struct inode *inode, struct file *file)
{
struct mdss_debug_base *dbg = file->private_data;
+ mutex_lock(&mdss_debug_lock);
if (dbg && dbg->buf) {
kfree(dbg->buf);
dbg->buf_len = 0;
dbg->buf = NULL;
}
+ mutex_unlock(&mdss_debug_lock);
return 0;
}
@@ -1788,6 +1828,24 @@ void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id,
}
+void mdss_dsi_debug_bus_init(struct mdss_dsi_data *sdata)
+{
+ if (!sdata)
+ return;
+
+ sdata->dbg_bus = NULL;
+ sdata->dbg_bus_size = 0;
+
+ switch (sdata->shared_data->hw_rev) {
+ case MDSS_DSI_HW_REV_201:
+ sdata->dbg_bus = dsi_dbg_bus_sdm660;
+ sdata->dbg_bus_size = ARRAY_SIZE(dsi_dbg_bus_sdm660);
+ break;
+ default:
+ break;
+ }
+}
+
int mdss_dump_misr_data(char **buf, u32 size)
{
struct mdss_mdp_misr_map *dsi0_map;
diff --git a/drivers/video/fbdev/msm/mdss_debug.h b/drivers/video/fbdev/msm/mdss_debug.h
index 1a44ab5c44a2..d778fb35ed2d 100644
--- a/drivers/video/fbdev/msm/mdss_debug.h
+++ b/drivers/video/fbdev/msm/mdss_debug.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -170,6 +170,7 @@ u32 get_dump_range(struct dump_offset *range_node, size_t max_offset);
void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag, char *addr,
int len, u32 **dump_mem, bool from_isr);
void mdss_mdp_debug_mid(u32 mid);
+void mdss_dump_dsi_debug_bus(u32 bus_dump_flag, u32 **dump_mem);
#else
struct mdss_debug_base;
struct dump_offset;
diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c
index 0a45ce036cf6..bf4117650e3c 100644
--- a/drivers/video/fbdev/msm/mdss_debug_xlog.c
+++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,6 +32,7 @@
#define XLOG_DEFAULT_REGDUMP 0x2 /* dump in RAM */
#define XLOG_DEFAULT_DBGBUSDUMP 0x2 /* dump in RAM */
#define XLOG_DEFAULT_VBIF_DBGBUSDUMP 0x2 /* dump in RAM */
+#define XLOG_DEFAULT_DSI_DBGBUSDUMP 0x2 /* dump in RAM */
/*
* xlog will print this number of entries when it is called through
@@ -73,14 +74,17 @@ struct mdss_dbg_xlog {
u32 enable_reg_dump;
u32 enable_dbgbus_dump;
u32 enable_vbif_dbgbus_dump;
+ u32 enable_dsi_dbgbus_dump;
struct work_struct xlog_dump_work;
struct mdss_debug_base *blk_arr[MDSS_DEBUG_BASE_MAX];
bool work_panic;
bool work_dbgbus;
bool work_vbif_dbgbus;
+ bool work_dsi_dbgbus;
u32 *dbgbus_dump; /* address for the debug bus dump */
u32 *vbif_dbgbus_dump; /* address for the vbif debug bus dump */
u32 *nrt_vbif_dbgbus_dump; /* address for the nrt vbif debug bus dump */
+ u32 *dsi_dbgbus_dump; /* address for the dsi debug bus dump */
} mdss_dbg_xlog;
static inline bool mdss_xlog_is_enabled(u32 flag)
@@ -579,7 +583,7 @@ struct mdss_debug_base *get_dump_blk_addr(const char *blk_name)
static void mdss_xlog_dump_array(struct mdss_debug_base *blk_arr[],
u32 len, bool dead, const char *name, bool dump_dbgbus,
- bool dump_vbif_dbgbus)
+ bool dump_vbif_dbgbus, bool dump_dsi_dbgbus)
{
int i;
@@ -603,6 +607,10 @@ static void mdss_xlog_dump_array(struct mdss_debug_base *blk_arr[],
&mdss_dbg_xlog.nrt_vbif_dbgbus_dump, false);
}
+ if (dump_dsi_dbgbus)
+ mdss_dump_dsi_debug_bus(mdss_dbg_xlog.enable_dsi_dbgbus_dump,
+ &mdss_dbg_xlog.dsi_dbgbus_dump);
+
if (dead && mdss_dbg_xlog.panic_on_err)
panic(name);
}
@@ -614,7 +622,8 @@ static void xlog_debug_work(struct work_struct *work)
ARRAY_SIZE(mdss_dbg_xlog.blk_arr),
mdss_dbg_xlog.work_panic, "xlog_workitem",
mdss_dbg_xlog.work_dbgbus,
- mdss_dbg_xlog.work_vbif_dbgbus);
+ mdss_dbg_xlog.work_vbif_dbgbus,
+ mdss_dbg_xlog.work_dsi_dbgbus);
}
void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
@@ -622,6 +631,7 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
int i, index = 0;
bool dead = false;
bool dump_dbgbus = false, dump_vbif_dbgbus = false;
+ bool dump_dsi_dbgbus = false;
va_list args;
char *blk_name = NULL;
struct mdss_debug_base *blk_base = NULL;
@@ -657,6 +667,9 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
if (!strcmp(blk_name, "vbif_dbg_bus"))
dump_vbif_dbgbus = true;
+ if (!strcmp(blk_name, "dsi_dbg_bus"))
+ dump_dsi_dbgbus = true;
+
if (!strcmp(blk_name, "panic"))
dead = true;
}
@@ -667,10 +680,11 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
mdss_dbg_xlog.work_panic = dead;
mdss_dbg_xlog.work_dbgbus = dump_dbgbus;
mdss_dbg_xlog.work_vbif_dbgbus = dump_vbif_dbgbus;
+ mdss_dbg_xlog.work_dsi_dbgbus = dump_dsi_dbgbus;
schedule_work(&mdss_dbg_xlog.xlog_dump_work);
} else {
mdss_xlog_dump_array(blk_arr, blk_len, dead, name, dump_dbgbus,
- dump_vbif_dbgbus);
+ dump_vbif_dbgbus, dump_dsi_dbgbus);
}
}
@@ -754,6 +768,7 @@ int mdss_create_xlog_debug(struct mdss_debug_data *mdd)
mdss_dbg_xlog.enable_reg_dump = XLOG_DEFAULT_REGDUMP;
mdss_dbg_xlog.enable_dbgbus_dump = XLOG_DEFAULT_DBGBUSDUMP;
mdss_dbg_xlog.enable_vbif_dbgbus_dump = XLOG_DEFAULT_VBIF_DBGBUSDUMP;
+ mdss_dbg_xlog.enable_dsi_dbgbus_dump = XLOG_DEFAULT_DSI_DBGBUSDUMP;
pr_info("xlog_status: enable:%d, panic:%d, dump:%d\n",
mdss_dbg_xlog.xlog_enable, mdss_dbg_xlog.panic_on_err,
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index 226198efbeec..1e878e9a00cb 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -69,6 +69,11 @@ static int mdss_dp_process_phy_test_pattern_request(
static int mdss_dp_send_audio_notification(
struct mdss_dp_drv_pdata *dp, int val);
+static inline void mdss_dp_reset_sink_count(struct mdss_dp_drv_pdata *dp)
+{
+ memset(&dp->sink_count, 0, sizeof(dp->sink_count));
+}
+
static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp)
{
dp->test_data = (const struct dpcd_test_request){ 0 };
@@ -133,22 +138,77 @@ static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name)
return !strncmp(clk_name, clk_prefix, strlen(clk_prefix));
}
+static void mdss_dp_reset_phy_config_indices(struct mdss_dp_drv_pdata *dp)
+{
+ int i = 0;
+
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+ dp->aux_cfg[i].current_index = 0;
+}
+
+static void mdss_dp_phy_aux_cfg_reset(struct mdss_dp_drv_pdata *dp)
+{
+ int i = 0;
+
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+ dp->aux_cfg[i] = (const struct mdss_dp_phy_cfg){ 0 };
+}
+
+static int mdss_dp_parse_aux_cfg(struct platform_device *pdev,
+ struct mdss_dp_drv_pdata *dp)
+{
+ int len = 0, i = 0, j = 0, config_count = 0;
+ const char *data;
+ int const minimum_config_count = 1;
+
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
+ const char *property = mdss_dp_get_phy_aux_config_property(i);
+
+ data = of_get_property(pdev->dev.of_node, property, &len);
+ if (!data) {
+ pr_err("Unable to read %s\n", property);
+ goto error;
+ }
+
+ config_count = len - 1;
+ if ((config_count < minimum_config_count) ||
+ (config_count > MDSS_DP_MAX_PHY_CFG_VALUE_CNT)) {
+ pr_err("Invalid config count (%d) configs for %s\n",
+ config_count, property);
+ goto error;
+ }
+
+ dp->aux_cfg[i].offset = data[0];
+ dp->aux_cfg[i].cfg_cnt = config_count;
+ pr_debug("%s offset=0x%x, cfg_cnt=%d\n",
+ property,
+ dp->aux_cfg[i].offset,
+ dp->aux_cfg[i].cfg_cnt);
+ for (j = 1; j < len; j++) {
+ dp->aux_cfg[i].lut[j - 1] = data[j];
+ pr_debug("%s lut[%d]=0x%x\n",
+ property,
+ i,
+ dp->aux_cfg[i].lut[j - 1]);
+ }
+ }
+
+ return 0;
+
+error:
+ mdss_dp_phy_aux_cfg_reset(dp);
+ return -EINVAL;
+}
+
static int mdss_dp_parse_prop(struct platform_device *pdev,
struct mdss_dp_drv_pdata *dp_drv)
{
int len = 0, i = 0, rc = 0;
const char *data;
- data = of_get_property(pdev->dev.of_node,
- "qcom,aux-cfg-settings", &len);
- if ((!data) || (len != AUX_CFG_LEN)) {
- pr_err("%s:%d, Unable to read DP AUX CFG settings",
- __func__, __LINE__);
- return -EINVAL;
- }
-
- for (i = 0; i < len; i++)
- dp_drv->aux_cfg[i] = data[i];
+ rc = mdss_dp_parse_aux_cfg(pdev, dp_drv);
+ if (rc)
+ return rc;
data = of_get_property(pdev->dev.of_node,
"qcom,logical2physical-lane-map", &len);
@@ -958,6 +1018,12 @@ void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp)
mdss_dp_configuration_ctrl(&dp->ctrl_io, data);
}
+static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val)
+{
+ if (dp && dp->ext_audio_data.intf_ops.notify)
+ dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val);
+}
+
static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv)
{
int ret = 0;
@@ -989,26 +1055,28 @@ static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv)
static void mdss_dp_update_cable_status(struct mdss_dp_drv_pdata *dp,
bool connected)
{
- mutex_lock(&dp->pd_msg_mutex);
+ mutex_lock(&dp->attention_lock);
pr_debug("cable_connected to %d\n", connected);
if (dp->cable_connected != connected)
dp->cable_connected = connected;
else
pr_debug("no change in cable status\n");
- mutex_unlock(&dp->pd_msg_mutex);
+ mutex_unlock(&dp->attention_lock);
}
static int dp_get_cable_status(struct platform_device *pdev, u32 vote)
{
- struct mdss_dp_drv_pdata *dp_ctrl = platform_get_drvdata(pdev);
+ struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev);
u32 hpd;
- if (!dp_ctrl) {
+ if (!dp) {
DEV_ERR("%s: invalid input\n", __func__);
return -ENODEV;
}
- hpd = dp_ctrl->cable_connected;
+ mutex_lock(&dp->attention_lock);
+ hpd = dp->cable_connected;
+ mutex_unlock(&dp->attention_lock);
return hpd;
}
@@ -1230,12 +1298,6 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic)
return 0;
} /* dp_init_panel_info */
-static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val)
-{
- if (dp && dp->ext_audio_data.intf_ops.notify)
- dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val);
-}
-
/**
* mdss_dp_get_lane_mapping() - returns lane mapping based on given orientation
* @orientation: usb plug orientation
@@ -1434,6 +1496,13 @@ static int mdss_dp_setup_main_link(struct mdss_dp_drv_pdata *dp, bool train)
if (ret)
goto end;
+ /*
+ * Skip the transfer unit setup and the routine to wait for the
+ * video ready interrupt as link training tests do not require
+ * video frames to be sent.
+ */
+ if (mdss_dp_is_link_training_requested(dp))
+ goto end;
send_video:
/*
* Set up transfer unit values and set controller state to send
@@ -1614,15 +1683,19 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
dp_drv = container_of(pdata, struct mdss_dp_drv_pdata,
panel_data);
- if (dp_drv->power_on) {
- /*
- * Acknowledge the connection event if link training has already
- * been done. This will unblock the external display thread and
- * allow the driver to progress. For example, in the case of
- * video test pattern requests, to send the test response and
- * start transmitting the test pattern.
- */
- mdss_dp_ack_state(dp_drv, true);
+ /*
+ * If the link already active, then nothing needs to be done here.
+ * However, it is possible that the the power_on flag could be
+ * set to true but we would still need to initialize the DP host.
+ * An example of this use-case is when a multiport dongle is connected
+ * and subsequently the downstream sink is disconnected. This would
+ * only go through the IRQ HPD path where we tear down the link but
+ * the power_on flag remains set to true. When the downstream sink
+ * is subsequently connected again, we need to re-initialize DP
+ * host
+ */
+ if (dp_drv->power_on &&
+ (dp_drv->new_vic && (dp_drv->new_vic == dp_drv->vic))) {
pr_debug("Link already setup, return\n");
return 0;
}
@@ -1640,6 +1713,23 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
return mdss_dp_on_hpd(dp_drv);
}
+static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp)
+{
+ return dp->dpcd.downstream_port.dfp_present;
+}
+
+static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp)
+{
+ return (mdss_dp_is_ds_bridge(dp) &&
+ (dp->sink_count.count == 0));
+}
+
+static bool mdss_dp_is_ds_bridge_no_local_edid(struct mdss_dp_drv_pdata *dp)
+{
+ return (mdss_dp_is_ds_bridge_sink_count_zero(dp) &&
+ !(dp->dpcd.flags & DPCD_PORT_0_EDID_PRESENTED));
+}
+
static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv)
{
if (!dp_drv->power_on) {
@@ -1657,10 +1747,16 @@ static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv)
/* Make sure DP mainlink and audio engines are disabled */
wmb();
- mdss_dp_ack_state(dp_drv, false);
+ /*
+ * If downstream device is a brige which no longer has any
+ * downstream devices connected to it, then we should reset
+ * the current panel info
+ */
+ if (mdss_dp_is_ds_bridge_sink_count_zero(dp_drv))
+ dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN);
+
mutex_unlock(&dp_drv->train_mutex);
- complete_all(&dp_drv->irq_comp);
pr_debug("end\n");
return 0;
@@ -1687,11 +1783,11 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv)
mdss_dp_host_deinit(dp_drv);
dp_drv->power_on = false;
- dp_drv->sink_info_read = false;
dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN);
- mdss_dp_ack_state(dp_drv, false);
mdss_dp_reset_test_data(dp_drv);
+ mdss_dp_reset_sink_count(dp_drv);
+ dp_drv->prev_sink_count = dp_drv->sink_count;
mutex_unlock(&dp_drv->train_mutex);
pr_debug("DP off done\n");
@@ -1730,8 +1826,9 @@ static int mdss_dp_send_audio_notification(
if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) {
dp->audio_test_req = false;
- pr_debug("sending audio notification\n");
flags |= MSM_EXT_DISP_HPD_AUDIO;
+ pr_debug("sending audio notification = %d, flags = %d\n", val,
+ flags);
if (dp->ext_audio_data.intf_ops.hpd)
ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
@@ -1756,7 +1853,8 @@ static int mdss_dp_send_video_notification(
goto end;
}
- flags |= MSM_EXT_DISP_HPD_VIDEO;
+ flags |= MSM_EXT_DISP_HPD_ASYNC_VIDEO;
+ pr_debug("sending video notification = %d, flags = %d\n", val, flags);
if (dp->ext_audio_data.intf_ops.hpd)
ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
@@ -1813,6 +1911,8 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
dp_drv->edid_buf = edid_init_data.buf;
dp_drv->edid_buf_size = edid_init_data.buf_size;
+ mdss_dp_set_default_resolution(dp_drv);
+
return 0;
}
@@ -1864,14 +1964,16 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
mdss_dp_ctrl_reset(&dp_drv->ctrl_io);
mdss_dp_phy_reset(&dp_drv->ctrl_io);
mdss_dp_aux_reset(&dp_drv->ctrl_io);
+ mdss_dp_aux_set_limits(&dp_drv->ctrl_io);
+
mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true);
pr_debug("Ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n",
mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io),
mdss_dp_get_phy_hw_version(&dp_drv->phy_io));
- mdss_dp_phy_aux_setup(&dp_drv->phy_io, dp_drv->aux_cfg,
- dp_drv->phy_reg_offset);
+ mdss_dp_reset_phy_config_indices(dp_drv);
+ mdss_dp_phy_aux_setup(dp_drv);
mdss_dp_irq_enable(dp_drv);
dp_drv->dp_initialized = true;
@@ -1939,10 +2041,11 @@ static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp)
static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
enum notification_status status)
{
- const int irq_comp_timeout = HZ * 2;
int ret = 0;
+ bool notify = false;
+ bool connect;
- mutex_lock(&dp->pd_msg_mutex);
+ pr_debug("beginning notification\n");
if (status == dp->hpd_notification_status) {
pr_debug("No change in status %s --> %s\n",
mdss_dp_notification_status_to_string(status),
@@ -1955,39 +2058,40 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
case NOTIFY_CONNECT_IRQ_HPD:
if (dp->hpd_notification_status != NOTIFY_DISCONNECT_IRQ_HPD)
goto invalid_request;
- /* Follow the same programming as for NOTIFY_CONNECT */
- mdss_dp_host_init(&dp->panel_data);
- mdss_dp_send_video_notification(dp, true);
+ notify = true;
+ connect = true;
break;
case NOTIFY_CONNECT:
- if ((dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) ||
- (dp->hpd_notification_status ==
- NOTIFY_DISCONNECT_IRQ_HPD))
+ if (dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD)
goto invalid_request;
- mdss_dp_host_init(&dp->panel_data);
- mdss_dp_send_video_notification(dp, true);
+ notify = true;
+ connect = true;
break;
case NOTIFY_DISCONNECT:
- mdss_dp_send_audio_notification(dp, false);
- mdss_dp_send_video_notification(dp, false);
+ /*
+ * Userspace triggers a disconnect event on boot up, this must
+ * not be processed as there was no previously connected sink
+ * device.
+ */
+ if (dp->hpd_notification_status == NOTIFY_UNKNOWN)
+ goto invalid_request;
+ if (dp->hpd_notification_status == NOTIFY_DISCONNECT_IRQ_HPD) {
+ /*
+ * user modules already turned off. Need to explicitly
+ * turn off DP core here.
+ */
+ mdss_dp_off_hpd(dp);
+ } else {
+ notify = true;
+ connect = false;
+ }
break;
case NOTIFY_DISCONNECT_IRQ_HPD:
if (dp->hpd_notification_status == NOTIFY_DISCONNECT)
goto invalid_request;
- mdss_dp_send_audio_notification(dp, false);
- mdss_dp_send_video_notification(dp, false);
- if (!IS_ERR_VALUE(ret) && ret) {
- reinit_completion(&dp->irq_comp);
- ret = wait_for_completion_timeout(&dp->irq_comp,
- irq_comp_timeout);
- if (ret <= 0) {
- pr_warn("irq_comp timed out\n");
- ret = -EINVAL;
- } else {
- ret = 0;
- }
- }
+ notify = true;
+ connect = false;
break;
default:
pr_err("Invalid notification status = %d\n", status);
@@ -1995,7 +2099,7 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
break;
}
- goto end;
+ goto notify;
invalid_request:
pr_err("Invalid request %s --> %s\n",
@@ -2004,16 +2108,34 @@ invalid_request:
mdss_dp_notification_status_to_string(status));
ret = -EINVAL;
-end:
+notify:
+ if (ret || !notify) {
+ pr_debug("not sending notification\n");
+ goto end;
+ }
+
+ atomic_set(&dp->notification_pending, 1);
+ if (connect) {
+ mdss_dp_host_init(&dp->panel_data);
+ ret = mdss_dp_send_video_notification(dp, true);
+ } else {
+ mdss_dp_send_audio_notification(dp, false);
+ ret = mdss_dp_send_video_notification(dp, false);
+ }
+
if (!ret) {
pr_debug("Successfully sent notification %s --> %s\n",
mdss_dp_notification_status_to_string(
dp->hpd_notification_status),
mdss_dp_notification_status_to_string(status));
- dp->hpd_notification_status = status;
+ } else {
+ pr_err("%s Notification failed\n",
+ mdss_dp_notification_status_to_string(status));
+ atomic_set(&dp->notification_pending, 0);
}
- mutex_unlock(&dp->pd_msg_mutex);
+end:
+ dp->hpd_notification_status = status;
return ret;
}
@@ -2022,9 +2144,6 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
int ret;
u32 max_pclk_khz;
- if (dp->sink_info_read)
- return 0;
-
pr_debug("start\n");
ret = mdss_dp_dpcd_cap_read(dp);
@@ -2037,8 +2156,25 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
*/
pr_err("dpcd read failed, set failsafe parameters\n");
mdss_dp_set_default_link_parameters(dp);
+ goto read_edid;
}
+ /*
+ * When connected to a multiport adaptor which does not have a
+ * local EDID present, do not attempt to read the EDID.
+ * When connected to a multiport adaptor with no downstream device
+ * connected to it, do not attempt to read the EDID. It is possible
+ * that the adaptor may advertise the presence of local EDID, but it
+ * is not guaranteed to work.
+ */
+ if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) {
+ if (mdss_dp_is_ds_bridge_no_local_edid(dp))
+ pr_debug("No local EDID present on DS branch device\n");
+ pr_info("no downstream devices, skip client notification\n");
+ goto end;
+ }
+
+read_edid:
ret = mdss_dp_edid_read(dp);
if (ret) {
pr_err("edid read error, setting default resolution\n");
@@ -2049,14 +2185,18 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data,
min(dp->max_pclk_khz, max_pclk_khz));
+ if (dp->dpcd_read_required) {
+ pr_debug("reading DPCD with updated AUX config\n");
+ mdss_dp_dpcd_cap_read(dp);
+ dp->dpcd_read_required = false;
+ }
+
ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data);
if (ret) {
pr_err("edid parse failed, setting default resolution\n");
goto notify;
}
- dp->sink_info_read = true;
-
notify:
if (ret) {
/* set failsafe parameters */
@@ -2083,7 +2223,6 @@ notify:
end:
pr_debug("end\n");
return ret;
-
}
static int mdss_dp_check_params(struct mdss_dp_drv_pdata *dp, void *arg)
@@ -2284,12 +2423,16 @@ static ssize_t mdss_dp_rda_connected(struct device *dev,
{
ssize_t ret;
struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev);
+ bool cable_connected;
if (!dp)
return -EINVAL;
- ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->cable_connected);
- pr_debug("%d\n", dp->cable_connected);
+ mutex_lock(&dp->attention_lock);
+ cable_connected = dp->cable_connected;
+ mutex_unlock(&dp->attention_lock);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", cable_connected);
+ pr_debug("%d\n", cable_connected);
return ret;
}
@@ -2458,6 +2601,7 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev,
int hpd, rc;
ssize_t ret = strnlen(buf, PAGE_SIZE);
struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev);
+ bool cable_connected;
if (!dp) {
pr_err("invalid data\n");
@@ -2473,9 +2617,13 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev,
}
dp->hpd = !!hpd;
- pr_debug("hpd=%d\n", dp->hpd);
+ mutex_lock(&dp->attention_lock);
+ cable_connected = dp->cable_connected;
+ mutex_unlock(&dp->attention_lock);
+ pr_debug("hpd=%d cable_connected=%s\n", dp->hpd,
+ cable_connected ? "true" : "false");
- if (dp->hpd && dp->cable_connected) {
+ if (dp->hpd && cable_connected) {
if (dp->alt_mode.current_state & DP_CONFIGURE_DONE) {
mdss_dp_host_init(&dp->panel_data);
mdss_dp_process_hpd_high(dp);
@@ -2805,8 +2953,6 @@ static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata)
/* wait until link training is completed */
mutex_lock(&dp_drv->train_mutex);
- mdss_dp_aux_set_sink_power_state(dp_drv, SINK_POWER_OFF);
-
reinit_completion(&dp_drv->idle_comp);
mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE);
if (!wait_for_completion_timeout(&dp_drv->idle_comp,
@@ -2896,28 +3042,33 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
switch (event) {
case MDSS_EVENT_UNBLANK:
- mdss_dp_ack_state(dp, true);
rc = mdss_dp_on(pdata);
break;
case MDSS_EVENT_PANEL_ON:
mdss_dp_update_hdcp_info(dp);
if (dp_is_hdcp_enabled(dp)) {
- cancel_delayed_work(&dp->hdcp_cb_work);
+ cancel_delayed_work_sync(&dp->hdcp_cb_work);
dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
queue_delayed_work(dp->workq,
&dp->hdcp_cb_work, HZ / 2);
}
break;
+ case MDSS_EVENT_POST_PANEL_ON:
+ atomic_set(&dp->notification_pending, 0);
+ complete_all(&dp->notification_comp);
+ break;
case MDSS_EVENT_PANEL_OFF:
rc = mdss_dp_off(pdata);
+ atomic_set(&dp->notification_pending, 0);
+ complete_all(&dp->notification_comp);
break;
case MDSS_EVENT_BLANK:
if (dp_is_hdcp_enabled(dp)) {
dp->hdcp_status = HDCP_STATE_INACTIVE;
- cancel_delayed_work(&dp->hdcp_cb_work);
+ cancel_delayed_work_sync(&dp->hdcp_cb_work);
if (dp->hdcp.ops->off)
dp->hdcp.ops->off(dp->hdcp.data);
}
@@ -3074,6 +3225,7 @@ static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev,
static void mdss_dp_video_ready(struct mdss_dp_drv_pdata *dp)
{
pr_debug("dp_video_ready\n");
+ mdss_dp_ack_state(dp, true);
complete(&dp->video_comp);
}
@@ -3105,10 +3257,21 @@ static int mdss_dp_event_thread(void *data)
ev_data = (struct mdss_dp_event_data *)data;
+ pr_debug("starting\n");
while (!kthread_should_stop()) {
wait_event(ev_data->event_q,
(ev_data->pndx != ev_data->gndx) ||
- kthread_should_stop());
+ kthread_should_stop() ||
+ kthread_should_park());
+ if (kthread_should_stop())
+ return 0;
+
+ if (kthread_should_park()) {
+ pr_debug("parking event thread\n");
+ kthread_parkme();
+ continue;
+ }
+
spin_lock_irqsave(&ev_data->event_lock, flag);
ev = &(ev_data->event_list[ev_data->gndx++]);
todo = ev->id;
@@ -3200,6 +3363,7 @@ irqreturn_t dp_isr(int irq, void *ptr)
spin_lock(&dp->lock);
isr1 = dp_read(base + DP_INTR_STATUS);
isr2 = dp_read(base + DP_INTR_STATUS2);
+ pr_debug("isr1=0x%08x, isr2=0x%08x\n", isr1, isr2);
mask1 = isr1 & dp->mask1;
@@ -3283,6 +3447,27 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
return 0;
}
+static void mdss_dp_reset_event_list(struct mdss_dp_drv_pdata *dp)
+{
+ struct mdss_dp_event_data *ev_data = &dp->dp_event;
+
+ spin_lock(&ev_data->event_lock);
+ ev_data->pndx = ev_data->gndx = 0;
+ spin_unlock(&ev_data->event_lock);
+
+ mutex_lock(&dp->attention_lock);
+ INIT_LIST_HEAD(&dp->attention_head);
+ mutex_unlock(&dp->attention_lock);
+}
+
+static void mdss_dp_reset_sw_state(struct mdss_dp_drv_pdata *dp)
+{
+ pr_debug("enter\n");
+ mdss_dp_reset_event_list(dp);
+ atomic_set(&dp->notification_pending, 0);
+ complete_all(&dp->notification_comp);
+}
+
static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr)
{
struct mdss_dp_drv_pdata *dp_drv;
@@ -3294,6 +3479,8 @@ static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr)
}
mdss_dp_update_cable_status(dp_drv, true);
+ if (dp_drv->ev_thread)
+ kthread_unpark(dp_drv->ev_thread);
if (dp_drv->hpd)
dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
@@ -3313,6 +3500,9 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr)
mdss_dp_update_cable_status(dp_drv, false);
dp_drv->alt_mode.current_state = UNKNOWN_STATE;
+ mdss_dp_reset_sw_state(dp_drv);
+ kthread_park(dp_drv->ev_thread);
+
/**
* Manually turn off the DP controller if we are in PHY
* testing mode.
@@ -3416,6 +3606,17 @@ static inline void mdss_dp_link_maintenance(struct mdss_dp_drv_pdata *dp,
if (mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD))
return;
+ if (atomic_read(&dp->notification_pending)) {
+ int ret;
+
+ pr_debug("waiting for the disconnect to finish\n");
+ ret = wait_for_completion_timeout(&dp->notification_comp, HZ);
+ if (ret <= 0) {
+ pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n");
+ return;
+ }
+ }
+
mdss_dp_on_irq(dp, lt_needed);
}
@@ -3567,7 +3768,7 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp)
return -EINVAL;
if (dp_is_hdcp_enabled(dp) && dp->hdcp.ops->off) {
- cancel_delayed_work(&dp->hdcp_cb_work);
+ cancel_delayed_work_sync(&dp->hdcp_cb_work);
dp->hdcp.ops->off(dp->hdcp.data);
}
@@ -3613,10 +3814,46 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp)
static int mdss_dp_process_downstream_port_status_change(
struct mdss_dp_drv_pdata *dp)
{
- if (!mdss_dp_is_downstream_port_status_changed(dp))
+ bool ds_status_changed = false;
+
+ if (mdss_dp_is_downstream_port_status_changed(dp)) {
+ pr_debug("downstream port status changed\n");
+ ds_status_changed = true;
+ }
+
+ /*
+ * Ideally sink should update the downstream port status changed
+ * whenever it updates the downstream sink count. However, it is
+ * possible that only the sink count is updated without setting
+ * the downstream port status changed bit.
+ */
+ if (dp->sink_count.count != dp->prev_sink_count.count) {
+ pr_debug("downstream sink count changed from %d --> %d\n",
+ dp->prev_sink_count.count, dp->sink_count.count);
+ ds_status_changed = true;
+ }
+
+ if (!ds_status_changed)
return -EINVAL;
- return mdss_dp_edid_read(dp);
+ mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD);
+ if (atomic_read(&dp->notification_pending)) {
+ int ret;
+
+ pr_debug("waiting for the disconnect to finish\n");
+ ret = wait_for_completion_timeout(&dp->notification_comp, HZ);
+ if (ret <= 0) {
+ pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) {
+ pr_debug("sink count is zero, nothing to do\n");
+ return 0;
+ }
+
+ return mdss_dp_process_hpd_high(dp);
}
static bool mdss_dp_video_pattern_test_lt_needed(struct mdss_dp_drv_pdata *dp)
@@ -3714,19 +3951,19 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
mdss_dp_aux_parse_sink_status_field(dp);
- ret = mdss_dp_process_link_training_request(dp);
+ ret = mdss_dp_process_downstream_port_status_change(dp);
if (!ret)
goto exit;
- ret = mdss_dp_process_phy_test_pattern_request(dp);
+ ret = mdss_dp_process_link_training_request(dp);
if (!ret)
goto exit;
- ret = mdss_dp_process_link_status_update(dp);
+ ret = mdss_dp_process_phy_test_pattern_request(dp);
if (!ret)
goto exit;
- ret = mdss_dp_process_downstream_port_status_change(dp);
+ ret = mdss_dp_process_link_status_update(dp);
if (!ret)
goto exit;
@@ -3739,7 +3976,6 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
goto exit;
pr_debug("done\n");
-
exit:
dp->hpd_irq_on = false;
return ret;
@@ -3785,7 +4021,8 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
node->vdo = *vdos;
mutex_lock(&dp_drv->attention_lock);
- list_add_tail(&node->list, &dp_drv->attention_head);
+ if (dp_drv->cable_connected)
+ list_add_tail(&node->list, &dp_drv->attention_head);
mutex_unlock(&dp_drv->attention_lock);
dp_send_events(dp_drv, EV_USBPD_ATTENTION);
@@ -3833,11 +4070,21 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv)
if (!dp_drv->alt_mode.dp_status.hpd_high) {
pr_debug("Attention: HPD low\n");
+ if (!dp_drv->power_on) {
+ pr_debug("HPD already low\n");
+ return;
+ }
+
if (dp_is_hdcp_enabled(dp_drv) && dp_drv->hdcp.ops->off) {
- cancel_delayed_work(&dp_drv->hdcp_cb_work);
+ cancel_delayed_work_sync(&dp_drv->hdcp_cb_work);
dp_drv->hdcp.ops->off(dp_drv->hdcp.data);
}
+ /*
+ * Reset the sink count before nofifying clients since HPD Low
+ * indicates that the downstream device has been disconnected.
+ */
+ mdss_dp_reset_sink_count(dp_drv);
mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT);
pr_debug("Attention: Notified clients\n");
@@ -3865,6 +4112,11 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv)
pr_debug("Attention: HPD high\n");
+ if (dp_drv->power_on) {
+ pr_debug("HPD high processed already\n");
+ return;
+ }
+
dp_drv->alt_mode.current_state |= DP_STATUS_DONE;
if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE) {
@@ -3886,7 +4138,13 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp)
pr_debug("processing item %d in the list\n", ++i);
+ reinit_completion(&dp->notification_comp);
mutex_lock(&dp->attention_lock);
+ if (!dp->cable_connected) {
+ pr_debug("cable disconnected, returning\n");
+ mutex_unlock(&dp->attention_lock);
+ goto exit;
+ }
node = list_first_entry(&dp->attention_head,
struct mdss_dp_attention_node, list);
@@ -3900,9 +4158,25 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp)
mdss_dp_usbpd_ext_dp_status(&dp->alt_mode.dp_status);
mdss_dp_process_attention(dp);
+ if (atomic_read(&dp->notification_pending)) {
+ pr_debug("waiting for the attention event to finish\n");
+ /*
+ * This wait is intentionally implemented without a
+ * timeout since this is happens only in possible error
+ * conditions e.g. if the display framework does not
+ * power off/on the DisplayPort device in time. Other
+ * events might already be queued from the sink at this
+ * point and they cannot be processed until the power
+ * off/on is complete otherwise we might have problems
+ * with interleaving of these events e.g. un-clocked
+ * register access.
+ */
+ wait_for_completion(&dp->notification_comp);
+ }
pr_debug("done processing item %d in the list\n", i);
};
+exit:
pr_debug("exit\n");
}
@@ -3978,7 +4252,6 @@ static int mdss_dp_probe(struct platform_device *pdev)
dp_drv->mask1 = EDP_INTR_MASK1;
dp_drv->mask2 = EDP_INTR_MASK2;
mutex_init(&dp_drv->emutex);
- mutex_init(&dp_drv->pd_msg_mutex);
mutex_init(&dp_drv->attention_lock);
mutex_init(&dp_drv->hdcp_mutex);
spin_lock_init(&dp_drv->lock);
@@ -4071,8 +4344,9 @@ static int mdss_dp_probe(struct platform_device *pdev)
dp_drv->inited = true;
dp_drv->hpd_irq_on = false;
+ atomic_set(&dp_drv->notification_pending, 0);
mdss_dp_reset_test_data(dp_drv);
- init_completion(&dp_drv->irq_comp);
+ init_completion(&dp_drv->notification_comp);
dp_drv->suspend_vic = HDMI_VFRMT_UNKNOWN;
pr_debug("done\n");
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index 4decb26ea073..f358aad8a667 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -77,7 +77,7 @@
#define EDP_INTR_I2C_NACK BIT(18)
#define EDP_INTR_I2C_DEFER BIT(21)
#define EDP_INTR_PLL_UNLOCKED BIT(24)
-#define EDP_INTR_AUX_ERROR BIT(27)
+#define EDP_INTR_PHY_AUX_ERR BIT(27)
#define EDP_INTR_STATUS1 \
@@ -85,7 +85,7 @@
EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \
EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \
EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \
- EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR)
+ EDP_INTR_PLL_UNLOCKED | EDP_INTR_PHY_AUX_ERR)
#define EDP_INTR_MASK1 (EDP_INTR_STATUS1 << 2)
@@ -110,6 +110,8 @@ struct edp_buf {
int len; /* dara length */
char trans_num; /* transaction number */
char i2c; /* 1 == i2c cmd, 0 == native cmd */
+ bool no_send_addr;
+ bool no_send_stop;
};
/* USBPD-TypeC specific Macros */
@@ -186,6 +188,7 @@ struct dp_alt_mode {
#define DPCD_MAX_DOWNSPREAD_0_5 BIT(2)
#define DPCD_NO_AUX_HANDSHAKE BIT(3)
#define DPCD_PORT_0_EDID_PRESENTED BIT(4)
+#define DPCD_PORT_1_EDID_PRESENTED BIT(5)
/* event */
#define EV_EDP_AUX_SETUP BIT(0)
@@ -239,6 +242,8 @@ struct downstream_port_config {
bool oui_support;
};
+#define DP_MAX_DS_PORT_COUNT 2
+
struct dpcd_cap {
char major;
char minor;
@@ -249,7 +254,7 @@ struct dpcd_cap {
char enhanced_frame;
u32 max_link_rate; /* 162, 270 and 540 Mb, divided by 10 */
u32 flags;
- u32 rx_port0_buf_size;
+ u32 rx_port_buf_size[DP_MAX_DS_PORT_COUNT];
u32 training_read_interval;/* us */
struct downstream_port_config downstream_port;
};
@@ -426,6 +431,102 @@ struct mdss_dp_crc_data {
u32 b_cb;
};
+#define MDSS_DP_MAX_PHY_CFG_VALUE_CNT 3
+struct mdss_dp_phy_cfg {
+ u32 cfg_cnt;
+ u32 current_index;
+ u32 offset;
+ u32 lut[MDSS_DP_MAX_PHY_CFG_VALUE_CNT];
+};
+
+/* PHY AUX config registers */
+enum dp_phy_aux_config_type {
+ PHY_AUX_CFG0,
+ PHY_AUX_CFG1,
+ PHY_AUX_CFG2,
+ PHY_AUX_CFG3,
+ PHY_AUX_CFG4,
+ PHY_AUX_CFG5,
+ PHY_AUX_CFG6,
+ PHY_AUX_CFG7,
+ PHY_AUX_CFG8,
+ PHY_AUX_CFG9,
+ PHY_AUX_CFG_MAX,
+};
+
+static inline const char *mdss_dp_get_phy_aux_config_property(u32 cfg_type)
+{
+ switch (cfg_type) {
+ case PHY_AUX_CFG0:
+ return "qcom,aux-cfg0-settings";
+ case PHY_AUX_CFG1:
+ return "qcom,aux-cfg1-settings";
+ case PHY_AUX_CFG2:
+ return "qcom,aux-cfg2-settings";
+ case PHY_AUX_CFG3:
+ return "qcom,aux-cfg3-settings";
+ case PHY_AUX_CFG4:
+ return "qcom,aux-cfg4-settings";
+ case PHY_AUX_CFG5:
+ return "qcom,aux-cfg5-settings";
+ case PHY_AUX_CFG6:
+ return "qcom,aux-cfg6-settings";
+ case PHY_AUX_CFG7:
+ return "qcom,aux-cfg7-settings";
+ case PHY_AUX_CFG8:
+ return "qcom,aux-cfg8-settings";
+ case PHY_AUX_CFG9:
+ return "qcom,aux-cfg9-settings";
+ default:
+ return "unknown";
+ }
+}
+
+static inline char *mdss_dp_phy_aux_config_type_to_string(u32 cfg_type)
+{
+ switch (cfg_type) {
+ case PHY_AUX_CFG0:
+ return DP_ENUM_STR(PHY_AUX_CFG0);
+ case PHY_AUX_CFG1:
+ return DP_ENUM_STR(PHY_AUX_CFG1);
+ case PHY_AUX_CFG2:
+ return DP_ENUM_STR(PHY_AUX_CFG2);
+ case PHY_AUX_CFG3:
+ return DP_ENUM_STR(PHY_AUX_CFG3);
+ case PHY_AUX_CFG4:
+ return DP_ENUM_STR(PHY_AUX_CFG4);
+ case PHY_AUX_CFG5:
+ return DP_ENUM_STR(PHY_AUX_CFG5);
+ case PHY_AUX_CFG6:
+ return DP_ENUM_STR(PHY_AUX_CFG6);
+ case PHY_AUX_CFG7:
+ return DP_ENUM_STR(PHY_AUX_CFG7);
+ case PHY_AUX_CFG8:
+ return DP_ENUM_STR(PHY_AUX_CFG8);
+ case PHY_AUX_CFG9:
+ return DP_ENUM_STR(PHY_AUX_CFG9);
+ default:
+ return "unknown";
+ }
+}
+
+enum dp_aux_transaction {
+ DP_AUX_WRITE,
+ DP_AUX_READ
+};
+
+static inline char *mdss_dp_aux_transaction_to_string(u32 transaction)
+{
+ switch (transaction) {
+ case DP_AUX_WRITE:
+ return DP_ENUM_STR(DP_AUX_WRITE);
+ case DP_AUX_READ:
+ return DP_ENUM_STR(DP_AUX_READ);
+ default:
+ return "unknown";
+ }
+}
+
struct mdss_dp_drv_pdata {
/* device driver */
int (*on) (struct mdss_panel_data *pdata);
@@ -449,11 +550,11 @@ struct mdss_dp_drv_pdata {
bool core_clks_on;
bool link_clks_on;
bool power_on;
- bool sink_info_read;
u32 suspend_vic;
bool hpd;
bool psm_enabled;
bool audio_test_req;
+ bool dpcd_read_required;
/* dp specific */
unsigned char *base;
@@ -513,10 +614,9 @@ struct mdss_dp_drv_pdata {
struct completion aux_comp;
struct completion idle_comp;
struct completion video_comp;
- struct completion irq_comp;
+ struct completion notification_comp;
struct mutex aux_mutex;
struct mutex train_mutex;
- struct mutex pd_msg_mutex;
struct mutex attention_lock;
struct mutex hdcp_mutex;
bool cable_connected;
@@ -540,13 +640,14 @@ struct mdss_dp_drv_pdata {
struct dp_statistic dp_stat;
bool hpd_irq_on;
u32 hpd_notification_status;
+ atomic_t notification_pending;
struct mdss_dp_event_data dp_event;
struct task_struct *ev_thread;
/* dt settings */
char l_map[4];
- u32 aux_cfg[AUX_CFG_LEN];
+ struct mdss_dp_phy_cfg aux_cfg[PHY_AUX_CFG_MAX];
struct workqueue_struct *workq;
struct delayed_work hdcp_cb_work;
@@ -562,6 +663,7 @@ struct mdss_dp_drv_pdata {
struct dpcd_test_request test_data;
struct dpcd_sink_count sink_count;
+ struct dpcd_sink_count prev_sink_count;
struct list_head attention_head;
};
@@ -688,6 +790,7 @@ enum dp_aux_error {
EDP_AUX_ERR_NACK = -3,
EDP_AUX_ERR_DEFER = -4,
EDP_AUX_ERR_NACK_DEFER = -5,
+ EDP_AUX_ERR_PHY = -6,
};
static inline char *mdss_dp_get_aux_error(u32 aux_error)
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 8566b1d6985a..37209c161366 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -73,6 +73,21 @@ static int dp_buf_trailing(struct edp_buf *eb)
return (int)(eb->end - eb->data);
}
+static void mdss_dp_aux_clear_hw_interrupts(void __iomem *phy_base)
+{
+ u32 data;
+
+ data = dp_read(phy_base + DP_PHY_AUX_INTERRUPT_STATUS);
+ pr_debug("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data);
+
+ dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
+ dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f);
+ dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0);
+
+ /* Ensure that all interrupts are cleared and acked */
+ wmb();
+}
+
/*
* edp aux dp_buf_add_cmd:
* NO native and i2c command mix allowed
@@ -123,35 +138,46 @@ static int dp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd)
return cmd->len - 1;
}
-static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base)
+static int dp_cmd_fifo_tx(struct mdss_dp_drv_pdata *dp)
{
u32 data;
- char *dp;
+ char *datap;
int len, cnt;
+ struct edp_buf *tp = &dp->txp;
+ void __iomem *base = dp->base;
+
len = tp->len; /* total byte to cmd fifo */
if (len == 0)
return 0;
cnt = 0;
- dp = tp->start;
+ datap = tp->start;
while (cnt < len) {
- data = *dp; /* data byte */
+ data = *datap; /* data byte */
data <<= 8;
data &= 0x00ff00; /* index = 0, write */
if (cnt == 0)
data |= BIT(31); /* INDEX_WRITE */
dp_write(base + DP_AUX_DATA, data);
cnt++;
- dp++;
+ datap++;
}
+ /* clear the current tx request before queuing a new one */
+ dp_write(base + DP_AUX_TRANS_CTRL, 0);
+
+ /* clear any previous PHY AUX interrupts */
+ mdss_dp_aux_clear_hw_interrupts(dp->phy_io.base);
+
data = (tp->trans_num - 1);
if (tp->i2c) {
data |= BIT(8); /* I2C */
- data |= BIT(10); /* NO SEND ADDR */
- data |= BIT(11); /* NO SEND STOP */
+ if (tp->no_send_addr)
+ data |= BIT(10); /* NO SEND ADDR */
+ if (tp->no_send_stop)
+ data |= BIT(11); /* NO SEND STOP */
}
data |= BIT(9); /* GO */
@@ -164,7 +190,7 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base)
{
u32 data;
char *dp;
- int i;
+ int i, actual_i;
data = 0; /* index = 0 */
data |= BIT(31); /* INDEX_WRITE */
@@ -177,7 +203,12 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base)
data = dp_read(base + DP_AUX_DATA);
for (i = 0; i < len; i++) {
data = dp_read(base + DP_AUX_DATA);
- *dp++ = (char)((data >> 8) & 0xff);
+ *dp++ = (char)((data >> 8) & 0xFF);
+
+ actual_i = (data >> 16) & 0xFF;
+ if (i != actual_i)
+ pr_warn("Index mismatch: expected %d, found %d\n",
+ i, actual_i);
}
rp->len = len;
@@ -214,9 +245,16 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep,
reinit_completion(&ep->aux_comp);
- len = dp_cmd_fifo_tx(&ep->txp, ep->base);
+ tp->no_send_addr = true;
+ tp->no_send_stop = true;
+ len = dp_cmd_fifo_tx(ep);
- wait_for_completion_timeout(&ep->aux_comp, HZ/4);
+ if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) {
+ pr_err("aux write timeout\n");
+ ep->aux_error_num = EDP_AUX_ERR_TOUT;
+ /* Reset the AUX controller state machine */
+ mdss_dp_aux_reset(&ep->ctrl_io);
+ }
if (ep->aux_error_num == EDP_AUX_ERR_NONE)
ret = len;
@@ -228,13 +266,6 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep,
return ret;
}
-int dp_aux_write(void *ep, struct edp_cmd *cmd)
-{
- int rc = dp_aux_write_cmds(ep, cmd);
-
- return rc < 0 ? -EINVAL : 0;
-}
-
static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
struct edp_cmd *cmds)
{
@@ -242,6 +273,7 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
struct edp_buf *tp;
struct edp_buf *rp;
int len, ret;
+ u32 data;
mutex_lock(&ep->aux_mutex);
ep->aux_cmd_busy = 1;
@@ -270,10 +302,23 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
reinit_completion(&ep->aux_comp);
- dp_cmd_fifo_tx(tp, ep->base);
+ tp->no_send_addr = true;
+ tp->no_send_stop = false;
+ dp_cmd_fifo_tx(ep);
- wait_for_completion_timeout(&ep->aux_comp, HZ/4);
+ if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) {
+ pr_err("aux read timeout\n");
+ ep->aux_error_num = EDP_AUX_ERR_TOUT;
+ /* Reset the AUX controller state machine */
+ mdss_dp_aux_reset(&ep->ctrl_io);
+ ret = ep->aux_error_num;
+ goto end;
+ }
+ /* clear the current rx request before queuing a new one */
+ data = dp_read(ep->base + DP_AUX_TRANS_CTRL);
+ data &= (~BIT(9));
+ dp_write(ep->base + DP_AUX_TRANS_CTRL, data);
if (ep->aux_error_num == EDP_AUX_ERR_NONE) {
ret = dp_cmd_fifo_rx(rp, len, ep->base);
@@ -284,58 +329,128 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
ret = ep->aux_error_num;
}
+end:
ep->aux_cmd_busy = 0;
mutex_unlock(&ep->aux_mutex);
return ret;
}
-int dp_aux_read(void *ep, struct edp_cmd *cmds)
-{
- int rc = dp_aux_read_cmds(ep, cmds);
-
- return rc < 0 ? -EINVAL : 0;
-}
-
void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
{
- if (isr & EDP_INTR_AUX_I2C_DONE)
+ pr_debug("isr=0x%08x\n", isr);
+ if (isr & EDP_INTR_AUX_I2C_DONE) {
ep->aux_error_num = EDP_AUX_ERR_NONE;
- else if (isr & EDP_INTR_WRONG_ADDR)
+ } else if (isr & EDP_INTR_WRONG_ADDR) {
ep->aux_error_num = EDP_AUX_ERR_ADDR;
- else if (isr & EDP_INTR_TIMEOUT)
+ } else if (isr & EDP_INTR_TIMEOUT) {
ep->aux_error_num = EDP_AUX_ERR_TOUT;
- if (isr & EDP_INTR_NACK_DEFER)
+ } else if (isr & EDP_INTR_NACK_DEFER) {
ep->aux_error_num = EDP_AUX_ERR_NACK;
+ } else if (isr & EDP_INTR_PHY_AUX_ERR) {
+ ep->aux_error_num = EDP_AUX_ERR_PHY;
+ mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base);
+ } else {
+ ep->aux_error_num = EDP_AUX_ERR_NONE;
+ }
complete(&ep->aux_comp);
}
void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
{
+ pr_debug("isr=0x%08x\n", isr);
if (isr & EDP_INTR_AUX_I2C_DONE) {
if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER))
ep->aux_error_num = EDP_AUX_ERR_NACK;
else
ep->aux_error_num = EDP_AUX_ERR_NONE;
} else {
- if (isr & EDP_INTR_WRONG_ADDR)
+ if (isr & EDP_INTR_WRONG_ADDR) {
ep->aux_error_num = EDP_AUX_ERR_ADDR;
- else if (isr & EDP_INTR_TIMEOUT)
+ } else if (isr & EDP_INTR_TIMEOUT) {
ep->aux_error_num = EDP_AUX_ERR_TOUT;
- if (isr & EDP_INTR_NACK_DEFER)
+ } else if (isr & EDP_INTR_NACK_DEFER) {
ep->aux_error_num = EDP_AUX_ERR_NACK_DEFER;
- if (isr & EDP_INTR_I2C_NACK)
+ } else if (isr & EDP_INTR_I2C_NACK) {
ep->aux_error_num = EDP_AUX_ERR_NACK;
- if (isr & EDP_INTR_I2C_DEFER)
+ } else if (isr & EDP_INTR_I2C_DEFER) {
ep->aux_error_num = EDP_AUX_ERR_DEFER;
+ } else if (isr & EDP_INTR_PHY_AUX_ERR) {
+ ep->aux_error_num = EDP_AUX_ERR_PHY;
+ mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base);
+ } else {
+ ep->aux_error_num = EDP_AUX_ERR_NONE;
+ }
}
complete(&ep->aux_comp);
}
-static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
- char *buf, int len, int i2c)
+static int dp_aux_rw_cmds_retry(struct mdss_dp_drv_pdata *dp,
+ struct edp_cmd *cmd, enum dp_aux_transaction transaction)
+{
+ int const retry_count = 5;
+ int adjust_count = 0;
+ int i;
+ u32 aux_cfg1_config_count;
+ int ret;
+
+ aux_cfg1_config_count = mdss_dp_phy_aux_get_config_cnt(dp,
+ PHY_AUX_CFG1);
+retry:
+ i = 0;
+ ret = 0;
+ do {
+ struct edp_cmd cmd1 = *cmd;
+
+ dp->aux_error_num = EDP_AUX_ERR_NONE;
+ pr_debug("Trying %s, iteration count: %d\n",
+ mdss_dp_aux_transaction_to_string(transaction),
+ i + 1);
+ if (transaction == DP_AUX_READ)
+ ret = dp_aux_read_cmds(dp, &cmd1);
+ else if (transaction == DP_AUX_WRITE)
+ ret = dp_aux_write_cmds(dp, &cmd1);
+
+ i++;
+ } while ((i < retry_count) && (ret < 0));
+
+ if (ret >= 0) /* rw success */
+ goto end;
+
+ if (adjust_count >= aux_cfg1_config_count) {
+ pr_err("PHY_AUX_CONFIG1 calibration failed\n");
+ goto end;
+ }
+
+ /* Adjust AUX configuration and retry */
+ pr_debug("AUX failure (%d), adjust AUX settings\n", ret);
+ mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
+ adjust_count++;
+ goto retry;
+
+end:
+ return ret;
+}
+
+/**
+ * dp_aux_write_buf_retry() - send a AUX write command
+ * @dp: display port driver data
+ * @addr: AUX address (in hex) to write the command to
+ * @buf: the buffer containing the actual payload
+ * @len: the length of the buffer @buf
+ * @i2c: indicates if it is an i2c-over-aux transaction
+ * @retry: specifies if retries should be attempted upon failures
+ *
+ * Send an AUX write command with the specified payload over the AUX
+ * channel. This function can send both native AUX command or an
+ * i2c-over-AUX command. In addition, if specified, it can also retry
+ * when failures are detected. The retry logic would adjust AUX PHY
+ * parameters on the fly.
+ */
+static int dp_aux_write_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr,
+ char *buf, int len, int i2c, bool retry)
{
struct edp_cmd cmd;
@@ -346,11 +461,42 @@ static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
cmd.len = len & 0x0ff;
cmd.next = 0;
- return dp_aux_write_cmds(ep, &cmd);
+ if (retry)
+ return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_WRITE);
+ else
+ return dp_aux_write_cmds(dp, &cmd);
}
-static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
- int len, int i2c)
+static int dp_aux_write_buf(struct mdss_dp_drv_pdata *dp, u32 addr,
+ char *buf, int len, int i2c)
+{
+ return dp_aux_write_buf_retry(dp, addr, buf, len, i2c, true);
+}
+
+int dp_aux_write(void *dp, struct edp_cmd *cmd)
+{
+ int rc = dp_aux_write_cmds(dp, cmd);
+
+ return rc < 0 ? -EINVAL : 0;
+}
+
+/**
+ * dp_aux_read_buf_retry() - send a AUX read command
+ * @dp: display port driver data
+ * @addr: AUX address (in hex) to write the command to
+ * @buf: the buffer containing the actual payload
+ * @len: the length of the buffer @buf
+ * @i2c: indicates if it is an i2c-over-aux transaction
+ * @retry: specifies if retries should be attempted upon failures
+ *
+ * Send an AUX write command with the specified payload over the AUX
+ * channel. This function can send both native AUX command or an
+ * i2c-over-AUX command. In addition, if specified, it can also retry
+ * when failures are detected. The retry logic would adjust AUX PHY
+ * parameters on the fly.
+ */
+static int dp_aux_read_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr,
+ int len, int i2c, bool retry)
{
struct edp_cmd cmd = {0};
@@ -361,7 +507,23 @@ static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
cmd.len = len & 0x0ff;
cmd.next = 0;
- return dp_aux_read_cmds(ep, &cmd);
+ if (retry)
+ return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_READ);
+ else
+ return dp_aux_read_cmds(dp, &cmd);
+}
+
+static int dp_aux_read_buf(struct mdss_dp_drv_pdata *dp, u32 addr,
+ int len, int i2c)
+{
+ return dp_aux_read_buf_retry(dp, addr, len, i2c, true);
+}
+
+int dp_aux_read(void *dp, struct edp_cmd *cmds)
+{
+ int rc = dp_aux_read_cmds(dp, cmds);
+
+ return rc < 0 ? -EINVAL : 0;
}
/*
@@ -733,16 +895,68 @@ static void dp_aux_send_checksum(struct mdss_dp_drv_pdata *dp, u32 checksum)
dp_aux_write_buf(dp, 0x260, data, 1, 0);
}
+int mdss_dp_aux_read_edid(struct mdss_dp_drv_pdata *dp,
+ u8 *buf, int size, int blk_num)
+{
+ int max_size_bytes = 16;
+ int rc, read_size;
+ int ret = 0;
+ u8 offset_lut[] = {0x0, 0x80};
+ u8 offset;
+
+ if (dp->test_data.test_requested == TEST_EDID_READ)
+ max_size_bytes = 128;
+
+ /*
+ * Calculate the offset of the desired EDID block to be read.
+ * For even blocks, offset starts at 0x0
+ * For odd blocks, offset starts at 0x80
+ */
+ if (blk_num % 2)
+ offset = offset_lut[1];
+ else
+ offset = offset_lut[0];
+
+ do {
+ struct edp_cmd cmd = {0};
+
+ read_size = min(size, max_size_bytes);
+ cmd.read = 1;
+ cmd.addr = EDID_START_ADDRESS;
+ cmd.len = read_size;
+ cmd.out_buf = buf;
+ cmd.i2c = 1;
+
+ /* Write the offset first prior to reading the data */
+ pr_debug("offset=0x%x, size=%d\n", offset, size);
+ dp_aux_write_buf_retry(dp, EDID_START_ADDRESS, &offset, 1, 1,
+ false);
+ rc = dp_aux_read(dp, &cmd);
+ if (rc < 0) {
+ pr_err("aux read failed\n");
+ return rc;
+ }
+
+ print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1,
+ buf, read_size, false);
+ buf += read_size;
+ offset += read_size;
+ size -= read_size;
+ ret += read_size;
+ } while (size > 0);
+
+ return ret;
+}
+
int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
{
- struct edp_buf *rp = &dp->rxp;
int rlen, ret = 0;
int edid_blk = 0, blk_num = 0, retries = 10;
bool edid_parsing_done = false;
- const u8 cea_tag = 0x02, start_ext_blk = 0x1;
u32 const segment_addr = 0x30;
u32 checksum = 0;
- char segment = 0x1;
+ bool phy_aux_update_requested = false;
+ bool ext_block_parsing_done = false;
ret = dp_aux_chan_ready(dp);
if (ret) {
@@ -750,72 +964,91 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
return ret;
}
+ memset(dp->edid_buf, 0, dp->edid_buf_size);
+
/**
* Parse the test request vector to see whether there is a
* TEST_EDID_READ test request.
*/
dp_sink_parse_test_request(dp);
- do {
- rlen = dp_aux_read_buf(dp, EDID_START_ADDRESS +
- (blk_num * EDID_BLOCK_SIZE),
- EDID_BLOCK_SIZE, 1);
+ while (retries) {
+ u8 segment;
+ u8 edid_buf[EDID_BLOCK_SIZE] = {0};
+
+ /*
+ * Write the segment first.
+ * Segment = 0, for blocks 0 and 1
+ * Segment = 1, for blocks 2 and 3
+ * Segment = 2, for blocks 3 and 4
+ * and so on ...
+ */
+ segment = blk_num >> 1;
+ dp_aux_write_buf_retry(dp, segment_addr, &segment, 1, 1, false);
+
+ rlen = mdss_dp_aux_read_edid(dp, edid_buf, EDID_BLOCK_SIZE,
+ blk_num);
if (rlen != EDID_BLOCK_SIZE) {
- pr_err("Read failed. rlen=%d\n", rlen);
+ pr_err("Read failed. rlen=%s\n",
+ mdss_dp_get_aux_error(rlen));
+ mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
+ phy_aux_update_requested = true;
+ retries--;
continue;
}
-
pr_debug("blk_num=%d, rlen=%d\n", blk_num, rlen);
-
- if (dp_edid_is_valid_header(rp->data)) {
- ret = dp_edid_buf_error(rp->data, rp->len);
+ print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1,
+ edid_buf, EDID_BLOCK_SIZE, false);
+ if (dp_edid_is_valid_header(edid_buf)) {
+ ret = dp_edid_buf_error(edid_buf, rlen);
if (ret) {
pr_err("corrupt edid block detected\n");
+ mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
+ phy_aux_update_requested = true;
+ retries--;
continue;
}
if (edid_parsing_done) {
+ pr_debug("block 0 parsed already\n");
blk_num++;
+ retries--;
continue;
}
- dp_extract_edid_manufacturer(&dp->edid, rp->data);
- dp_extract_edid_product(&dp->edid, rp->data);
- dp_extract_edid_version(&dp->edid, rp->data);
- dp_extract_edid_ext_block_cnt(&dp->edid, rp->data);
- dp_extract_edid_video_support(&dp->edid, rp->data);
- dp_extract_edid_feature(&dp->edid, rp->data);
+ dp_extract_edid_manufacturer(&dp->edid, edid_buf);
+ dp_extract_edid_product(&dp->edid, edid_buf);
+ dp_extract_edid_version(&dp->edid, edid_buf);
+ dp_extract_edid_ext_block_cnt(&dp->edid, edid_buf);
+ dp_extract_edid_video_support(&dp->edid, edid_buf);
+ dp_extract_edid_feature(&dp->edid, edid_buf);
dp_extract_edid_detailed_timing_description(&dp->edid,
- rp->data);
+ edid_buf);
edid_parsing_done = true;
+ } else if (!edid_parsing_done) {
+ pr_debug("Invalid edid block 0 header\n");
+ /* Retry block 0 with adjusted phy aux settings */
+ mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
+ phy_aux_update_requested = true;
+ retries--;
+ continue;
} else {
edid_blk++;
blk_num++;
-
- /* fix dongle byte shift issue */
- if (edid_blk == 1 && rp->data[0] != cea_tag) {
- u8 tmp[EDID_BLOCK_SIZE - 1];
-
- memcpy(tmp, rp->data, EDID_BLOCK_SIZE - 1);
- rp->data[0] = cea_tag;
- memcpy(rp->data + 1, tmp, EDID_BLOCK_SIZE - 1);
- }
}
memcpy(dp->edid_buf + (edid_blk * EDID_BLOCK_SIZE),
- rp->data, EDID_BLOCK_SIZE);
+ edid_buf, EDID_BLOCK_SIZE);
- checksum = rp->data[rp->len - 1];
+ checksum = edid_buf[rlen - 1];
/* break if no more extension blocks present */
- if (edid_blk == dp->edid.ext_block_cnt)
+ if (edid_blk >= dp->edid.ext_block_cnt) {
+ ext_block_parsing_done = true;
break;
-
- /* write segment number to read block 3 onwards */
- if (edid_blk == start_ext_blk)
- dp_aux_write_buf(dp, segment_addr, &segment, 1, 1);
- } while (retries--);
+ }
+ }
if (dp->test_data.test_requested == TEST_EDID_READ) {
pr_debug("sending checksum %d\n", checksum);
@@ -823,6 +1056,18 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
dp->test_data = (const struct dpcd_test_request){ 0 };
}
+ /*
+ * Trigger the reading of DPCD if there was a change in the AUX
+ * configuration caused by a failure while reading the EDID.
+ * This is required to ensure the integrity and validity
+ * of the sink capabilities read that will subsequently be used
+ * to establish the mainlink.
+ */
+ if (edid_parsing_done && ext_block_parsing_done
+ && phy_aux_update_requested) {
+ dp->dpcd_read_required = true;
+ }
+
return ret;
}
@@ -834,6 +1079,10 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
struct dpcd_cap *cap;
struct edp_buf *rp;
int rlen;
+ int i;
+
+ cap = &ep->dpcd;
+ memset(cap, 0, sizeof(*cap));
rlen = dp_aux_read_buf(ep, 0, len, 0);
if (rlen <= 0) {
@@ -848,11 +1097,8 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
}
rp = &ep->rxp;
- cap = &ep->dpcd;
bp = rp->data;
- memset(cap, 0, sizeof(*cap));
-
data = *bp++; /* byte 0 */
cap->major = (data >> 4) & 0x0f;
cap->minor = data & 0x0f;
@@ -909,6 +1155,11 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */
cap->downstream_port.dfp_count = data & 0x7;
+ if (cap->downstream_port.dfp_count > DP_MAX_DS_PORT_COUNT) {
+ pr_debug("DS port count %d greater that max (%d) supported\n",
+ cap->downstream_port.dfp_count, DP_MAX_DS_PORT_COUNT);
+ cap->downstream_port.dfp_count = DP_MAX_DS_PORT_COUNT;
+ }
cap->downstream_port.msa_timing_par_ignored = data & BIT(6);
cap->downstream_port.oui_support = data & BIT(7);
pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n",
@@ -916,17 +1167,23 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
cap->downstream_port.msa_timing_par_ignored);
pr_debug("oui_support = %d\n", cap->downstream_port.oui_support);
- data = *bp++; /* byte 8 */
- if (data & BIT(1)) {
- cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
- pr_debug("edid presented\n");
- }
-
- data = *bp++; /* byte 9 */
- cap->rx_port0_buf_size = (data + 1) * 32;
- pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size);
+ for (i = 0; i < DP_MAX_DS_PORT_COUNT; i++) {
+ data = *bp++; /* byte 8 + i*2 */
+ pr_debug("parsing capabilities for DS port %d\n", i);
+ if (data & BIT(1)) {
+ if (i == 0)
+ cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
+ else
+ cap->flags |= DPCD_PORT_1_EDID_PRESENTED;
+ pr_debug("local edid present\n");
+ } else {
+ pr_debug("local edid absent\n");
+ }
- bp += 2; /* skip 10, 11 port1 capability */
+ data = *bp++; /* byte 9 + i*2 */
+ cap->rx_port_buf_size[i] = (data + 1) * 32;
+ pr_debug("lane_buf_size=%d\n", cap->rx_port_buf_size[i]);
+ }
data = *bp++; /* byte 12 */
cap->i2c_speed_ctrl = data;
@@ -1258,6 +1515,8 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep)
int const param_len = 0x1;
int const sink_count_addr = 0x200;
+ ep->prev_sink_count = ep->sink_count;
+
rlen = dp_aux_read_buf(ep, sink_count_addr, param_len, 0);
if (rlen < param_len) {
pr_err("failed to read sink count\n");
@@ -2363,8 +2622,8 @@ clear:
void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep)
{
dp_sink_parse_sink_count(ep);
- dp_sink_parse_test_request(ep);
mdss_dp_aux_link_status_read(ep, 6);
+ dp_sink_parse_test_request(ep);
}
int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep)
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index ea492f54054c..0d9cf7b72b4d 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -858,6 +858,48 @@ void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
pr_debug("dp_tu=0x%x\n", dp_tu);
}
+void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io)
+{
+ u32 const max_aux_timeout_count = 0xFFFFF;
+ u32 const max_aux_limits = 0xFFFFFFFF;
+
+ pr_debug("timeout=0x%x, limits=0x%x\n",
+ max_aux_timeout_count, max_aux_limits);
+
+ writel_relaxed(max_aux_timeout_count,
+ ctrl_io->base + DP_AUX_TIMEOUT_COUNT);
+ writel_relaxed(max_aux_limits, ctrl_io->base + DP_AUX_LIMITS);
+}
+
+void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp,
+ enum dp_phy_aux_config_type config_type)
+{
+ u32 new_index;
+ struct dss_io_data *phy_io = &dp->phy_io;
+ struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp,
+ config_type);
+
+ if (!cfg) {
+ pr_err("invalid config type %s",
+ mdss_dp_phy_aux_config_type_to_string(config_type));
+ return;
+ }
+
+ new_index = (cfg->current_index + 1) % cfg->cfg_cnt;
+
+ pr_debug("Updating %s from 0x%08x to 0x%08x\n",
+ mdss_dp_phy_aux_config_type_to_string(config_type),
+ cfg->lut[cfg->current_index], cfg->lut[new_index]);
+ writel_relaxed(cfg->lut[new_index], phy_io->base + cfg->offset);
+ cfg->current_index = new_index;
+
+ /* Make sure the new HW configuration takes effect */
+ wmb();
+
+ /* Reset the AUX controller before any subsequent transactions */
+ mdss_dp_aux_reset(&dp->ctrl_io);
+}
+
void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map)
{
u8 bits_per_lane = 2;
@@ -870,26 +912,24 @@ void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map)
ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING);
}
-void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg,
- u32 phy_reg_offset)
+void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp)
{
- void __iomem *adjusted_phy_io_base = phy_io->base + phy_reg_offset;
+ int i;
+ void __iomem *adjusted_phy_io_base = dp->phy_io.base +
+ dp->phy_reg_offset;
writel_relaxed(0x3d, adjusted_phy_io_base + DP_PHY_PD_CTL);
- /* DP AUX CFG register programming */
- writel_relaxed(aux_cfg[0], adjusted_phy_io_base + DP_PHY_AUX_CFG0);
- writel_relaxed(aux_cfg[1], adjusted_phy_io_base + DP_PHY_AUX_CFG1);
- writel_relaxed(aux_cfg[2], adjusted_phy_io_base + DP_PHY_AUX_CFG2);
- writel_relaxed(aux_cfg[3], adjusted_phy_io_base + DP_PHY_AUX_CFG3);
- writel_relaxed(aux_cfg[4], adjusted_phy_io_base + DP_PHY_AUX_CFG4);
- writel_relaxed(aux_cfg[5], adjusted_phy_io_base + DP_PHY_AUX_CFG5);
- writel_relaxed(aux_cfg[6], adjusted_phy_io_base + DP_PHY_AUX_CFG6);
- writel_relaxed(aux_cfg[7], adjusted_phy_io_base + DP_PHY_AUX_CFG7);
- writel_relaxed(aux_cfg[8], adjusted_phy_io_base + DP_PHY_AUX_CFG8);
- writel_relaxed(aux_cfg[9], adjusted_phy_io_base + DP_PHY_AUX_CFG9);
-
- writel_relaxed(0x1f, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK);
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
+ struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp, i);
+
+ pr_debug("%s: offset=0x%08x, value=0x%08x\n",
+ mdss_dp_phy_aux_config_type_to_string(i), cfg->offset,
+ cfg->lut[cfg->current_index]);
+ writel_relaxed(cfg->lut[cfg->current_index],
+ dp->phy_io.base + cfg->offset);
+ };
+ writel_relaxed(0x1e, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK);
}
int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv)
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index 8f19e7cdf3cf..4c93e48e97dc 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -35,6 +35,8 @@
#define DP_AUX_CTRL (0x00000230)
#define DP_AUX_DATA (0x00000234)
#define DP_AUX_TRANS_CTRL (0x00000238)
+#define DP_AUX_TIMEOUT_COUNT (0x0000023C)
+#define DP_AUX_LIMITS (0x00000240)
#define DP_AUX_STATUS (0x00000244)
#define DP_DPCD_CP_IRQ (0x201)
@@ -163,6 +165,7 @@
#define DP_PHY_AUX_CFG9 (0x00000040)
#define DP_PHY_AUX_INTERRUPT_MASK (0x00000044)
#define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048)
+#define DP_PHY_AUX_INTERRUPT_STATUS (0x000000B8)
#define DP_PHY_SPARE0 0x00A8
@@ -271,6 +274,19 @@ static const struct dp_vc_tu_mapping_table tu_table[] = {
0x21, 0x000c, false, 0x00, 0x00, 0x00, 0x27},
};
+static inline struct mdss_dp_phy_cfg *mdss_dp_phy_aux_get_config(
+ struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type)
+{
+ return &dp->aux_cfg[cfg_type];
+}
+
+static inline u32 mdss_dp_phy_aux_get_config_cnt(
+ struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type)
+{
+ return dp->aux_cfg[cfg_type].cfg_cnt;
+}
+
+void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io);
int dp_aux_read(void *ep, struct edp_cmd *cmds);
int dp_aux_write(void *ep, struct edp_cmd *cmd);
void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data);
@@ -285,8 +301,9 @@ void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert);
void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
u8 ln_cnt, u32 res, struct mdss_panel_info *pinfo);
void mdss_dp_config_misc(struct mdss_dp_drv_pdata *dp, u32 bd, u32 cc);
-void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg,
- u32 phy_reg_offset);
+void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp);
+void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp,
+ enum dp_phy_aux_config_type config_type);
void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 17722eac3006..c7cac996e5c0 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -26,6 +26,7 @@
#include <linux/uaccess.h>
#include <linux/msm-bus.h>
#include <linux/pm_qos.h>
+#include <linux/dma-buf.h>
#include "mdss.h"
#include "mdss_panel.h"
@@ -44,6 +45,97 @@ static struct mdss_dsi_data *mdss_dsi_res;
static struct pm_qos_request mdss_dsi_pm_qos_request;
+void mdss_dump_dsi_debug_bus(u32 bus_dump_flag,
+ u32 **dump_mem)
+{
+ struct mdss_dsi_data *sdata = mdss_dsi_res;
+ struct mdss_dsi_ctrl_pdata *m_ctrl, *s_ctrl;
+ bool in_log, in_mem;
+ u32 *dump_addr = NULL;
+ u32 status0 = 0, status1 = 0;
+ phys_addr_t phys = 0;
+ int list_size = 0;
+ int i;
+ bool dsi0_active = false, dsi1_active = false;
+
+ if (!sdata || !sdata->dbg_bus || !sdata->dbg_bus_size)
+ return;
+
+ m_ctrl = sdata->ctrl_pdata[0];
+ s_ctrl = sdata->ctrl_pdata[1];
+
+ if (!m_ctrl)
+ return;
+
+ if (m_ctrl && m_ctrl->shared_data->dsi0_active)
+ dsi0_active = true;
+ if (s_ctrl && s_ctrl->shared_data->dsi1_active)
+ dsi1_active = true;
+
+ list_size = (sdata->dbg_bus_size * sizeof(sdata->dbg_bus[0]) * 4);
+
+ in_log = (bus_dump_flag & MDSS_DBG_DUMP_IN_LOG);
+ in_mem = (bus_dump_flag & MDSS_DBG_DUMP_IN_MEM);
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(&sdata->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");
+ }
+ }
+
+ pr_info("========= Start DSI Debug Bus =========\n");
+
+ mdss_dsi_clk_ctrl(m_ctrl, m_ctrl->dsi_clk_handle,
+ MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON);
+
+ for (i = 0; i < sdata->dbg_bus_size; i++) {
+ if (dsi0_active) {
+ writel_relaxed(sdata->dbg_bus[i],
+ m_ctrl->ctrl_base + 0x124);
+ wmb(); /* ensure regsiter is committed */
+ }
+ if (dsi1_active) {
+ writel_relaxed(sdata->dbg_bus[i],
+ s_ctrl->ctrl_base + 0x124);
+ wmb(); /* ensure register is committed */
+ }
+
+ if (dsi0_active) {
+ status0 = readl_relaxed(m_ctrl->ctrl_base + 0x128);
+ if (in_log)
+ pr_err("CTRL:0 bus_ctrl: 0x%x status: 0x%x\n",
+ sdata->dbg_bus[i], status0);
+ }
+ if (dsi1_active) {
+ status1 = readl_relaxed(s_ctrl->ctrl_base + 0x128);
+ if (in_log)
+ pr_err("CTRL:1 bus_ctrl: 0x%x status: 0x%x\n",
+ sdata->dbg_bus[i], status1);
+ }
+
+ if (dump_addr && in_mem) {
+ dump_addr[i*4] = sdata->dbg_bus[i];
+ dump_addr[i*4 + 1] = status0;
+ dump_addr[i*4 + 2] = status1;
+ dump_addr[i*4 + 3] = 0x0;
+ }
+ }
+
+ mdss_dsi_clk_ctrl(m_ctrl, m_ctrl->dsi_clk_handle,
+ MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF);
+
+ pr_info("========End DSI Debug Bus=========\n");
+}
+
static void mdss_dsi_pm_qos_add_request(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
struct irq_info *irq_info;
@@ -570,7 +662,11 @@ static int mdss_dsi_get_dt_vreg_data(struct device *dev,
mp->vreg_config[i].post_off_sleep = tmp;
}
- pr_debug("%s: %s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n",
+ mp->vreg_config[i].lp_disable_allowed =
+ of_property_read_bool(supply_node,
+ "qcom,supply-lp-mode-disable-allowed");
+
+ pr_debug("%s: %s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d lp_disable_allowed=%d\n",
__func__,
mp->vreg_config[i].vreg_name,
mp->vreg_config[i].min_voltage,
@@ -580,8 +676,8 @@ static int mdss_dsi_get_dt_vreg_data(struct device *dev,
mp->vreg_config[i].pre_on_sleep,
mp->vreg_config[i].post_on_sleep,
mp->vreg_config[i].pre_off_sleep,
- mp->vreg_config[i].post_off_sleep
- );
+ mp->vreg_config[i].post_off_sleep,
+ mp->vreg_config[i].lp_disable_allowed);
++i;
}
@@ -2713,10 +2809,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dsi_reconfig(pdata, mode);
break;
case MDSS_EVENT_DSI_PANEL_STATUS:
- if (ctrl_pdata->check_status)
- rc = ctrl_pdata->check_status(ctrl_pdata);
- else
- rc = true;
+ rc = mdss_dsi_check_panel_status(ctrl_pdata, arg);
break;
case MDSS_EVENT_PANEL_TIMING_SWITCH:
rc = mdss_dsi_panel_timing_switch(ctrl_pdata, arg);
@@ -3359,6 +3452,8 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
else
ctrl_pdata->shared_data->dsi1_active = true;
+ mdss_dsi_debug_bus_init(mdss_dsi_res);
+
return 0;
error_shadow_clk_deinit:
@@ -4269,6 +4364,9 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev,
return rc;
}
+ /* default state of gpio is false */
+ ctrl_pdata->bklt_en_gpio_state = false;
+
pinfo->panel_max_fps = mdss_panel_get_framerate(pinfo);
pinfo->panel_max_vtotal = mdss_panel_get_vtotal(pinfo);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 2a76466abf3e..62d88f0af652 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -315,6 +315,8 @@ struct mdss_dsi_data {
* mutex, clocks, regulator information, setup information
*/
struct dsi_shared_data *shared_data;
+ u32 *dbg_bus;
+ int dbg_bus_size;
};
/*
@@ -452,6 +454,7 @@ struct mdss_dsi_ctrl_pdata {
int disp_en_gpio;
int bklt_en_gpio;
bool bklt_en_gpio_invert;
+ bool bklt_en_gpio_state;
int lcd_mode_sel_gpio;
int bklt_ctrl; /* backlight ctrl */
bool pwm_pmi;
@@ -704,6 +707,9 @@ void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off,
u32 mask, u32 val);
int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl);
+int mdss_dsi_check_panel_status(struct mdss_dsi_ctrl_pdata *ctrl, void *arg);
+
+void mdss_dsi_debug_bus_init(struct mdss_dsi_data *sdata);
static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module)
{
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index 9f4b7eb52492..1a471155072b 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -778,6 +778,12 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
u32 loop = 10, u_dly = 200;
pr_debug("%s: MDSS DSI CTRL and PHY reset. ctrl-num = %d\n",
__func__, ctrl->ndx);
+
+ if (ctrl->panel_mode == DSI_CMD_MODE) {
+ pr_warn("ctl_phy_reset not applicable for cmd mode\n");
+ return;
+ }
+
if (event == DSI_EV_DLNx_FIFO_OVERFLOW) {
mask = BIT(20); /* clock lane only for overflow recovery */
} else if (event == DSI_EV_LP_RX_TIMEOUT) {
@@ -792,15 +798,6 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0);
ctrl1 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_1);
- if (ctrl0->recovery) {
- rc = ctrl0->recovery->fxn(ctrl0->recovery->data,
- MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
- if (rc < 0) {
- pr_debug("%s: Target is in suspend/shutdown\n",
- __func__);
- return;
- }
- }
/*
* Disable PHY contention detection and receive.
* Configure the strength ctrl 1 register.
@@ -874,6 +871,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
MIPI_OUTP(ctrl0->ctrl_base + 0x0ac, ln_ctrl0 & ~mask);
MIPI_OUTP(ctrl1->ctrl_base + 0x0ac, ln_ctrl1 & ~mask);
+ if (ctrl0->recovery) {
+ rc = ctrl0->recovery->fxn(ctrl0->recovery->data,
+ MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
+ if (rc < 0) {
+ pr_debug("%s: Target is in suspend/shutdown\n",
+ __func__);
+ return;
+ }
+ }
/* Enable Video mode for DSI controller */
MIPI_OUTP(ctrl0->ctrl_base + 0x004, data0);
MIPI_OUTP(ctrl1->ctrl_base + 0x004, data1);
@@ -890,15 +896,6 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
*/
udelay(200);
} else {
- if (ctrl->recovery) {
- rc = ctrl->recovery->fxn(ctrl->recovery->data,
- MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
- if (rc < 0) {
- pr_debug("%s: Target is in suspend/shutdown\n",
- __func__);
- return;
- }
- }
/* Disable PHY contention detection and receive */
MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0);
@@ -951,6 +948,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
__func__, ln0);
MIPI_OUTP(ctrl->ctrl_base + 0x0ac, ln_ctrl0 & ~mask);
+ if (ctrl->recovery) {
+ rc = ctrl->recovery->fxn(ctrl->recovery->data,
+ MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
+ if (rc < 0) {
+ pr_debug("%s: Target is in suspend/shutdown\n",
+ __func__);
+ return;
+ }
+ }
/* Enable Video mode for DSI controller */
MIPI_OUTP(ctrl->ctrl_base + 0x004, data0);
/* Enable PHY contention detection and receiver */
@@ -1311,6 +1317,31 @@ void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl)
}
+static void mdss_dsi_split_link_setup(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
+{
+ u32 data = 0;
+ struct mdss_panel_info *pinfo;
+
+ if (!ctrl_pdata)
+ return;
+
+ pinfo = &ctrl_pdata->panel_data.panel_info;
+ if (!pinfo->split_link_enabled)
+ return;
+
+ pr_debug("%s: enable split link\n", __func__);
+
+ data = MIPI_INP((ctrl_pdata->ctrl_base) + 0x330);
+ /* DMA_LINK_SEL */
+ data |= 0x3 << 12;
+ /* MDP0_LINK_SEL */
+ data |= 0x5 << 20;
+ /* EN */
+ data |= 0x1;
+ /* DSI_SPLIT_LINK_CTRL */
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x330, data);
+}
+
static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
@@ -1429,6 +1460,8 @@ static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata)
}
mdss_dsi_dsc_config(ctrl_pdata, dsc);
+
+ mdss_dsi_split_link_setup(ctrl_pdata);
}
void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl)
@@ -1529,11 +1562,13 @@ wait:
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl",
"dsi0_phy", "dsi1_ctrl", "dsi1_phy",
"vbif", "vbif_nrt", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
/* mask out overflow errors */
if (ignore_underflow)
mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000);
+
+ MDSS_XLOG(ctrl_pdata->ndx, ctrl_pdata->mdp_busy);
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x098, 0x01); /* trigger */
wmb();
@@ -2152,6 +2187,7 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01);
wmb();
+ MDSS_XLOG(ctrl->dma_addr, len);
if (ctrl->do_unicast) {
/* let cmd_trigger to kickoff later */
@@ -2528,7 +2564,7 @@ void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl)
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl",
"dsi0_phy", "dsi1_ctrl", "dsi1_phy",
"vbif", "vbif_nrt", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
}
}
}
@@ -2998,7 +3034,10 @@ bool mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl)
* warning message is ignored.
*/
if (ctrl->panel_data.panel_info.esd_check_enabled &&
- (ctrl->status_mode == ESD_BTA) && (status & 0x1008000))
+ ((ctrl->status_mode == ESD_BTA) ||
+ (ctrl->status_mode == ESD_REG) ||
+ (ctrl->status_mode == ESD_REG_NT35596)) &&
+ (status & 0x1008000))
return false;
pr_err("%s: status=%x\n", __func__, status);
@@ -3151,7 +3190,7 @@ static void __dsi_error_counter(struct dsi_err_container *err_container)
pr_err("%s: panic in WQ as dsi error intrs within:%dms\n",
__func__, err_container->err_time_delta);
MDSS_XLOG_TOUT_HANDLER_WQ("mdp", "dsi0_ctrl", "dsi0_phy",
- "dsi1_ctrl", "dsi1_phy", "panic");
+ "dsi1_ctrl", "dsi1_phy", "dsi_dbg_bus", "panic");
}
}
@@ -3229,8 +3268,10 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr)
* cleared.
*/
if (ctrl->panel_data.panel_info.esd_check_enabled &&
- (ctrl->status_mode == ESD_BTA) &&
- (ctrl->panel_mode == DSI_VIDEO_MODE)) {
+ ((ctrl->status_mode == ESD_BTA) ||
+ (ctrl->status_mode == ESD_REG) ||
+ (ctrl->status_mode == ESD_REG_NT35596)) &&
+ (ctrl->panel_mode == DSI_VIDEO_MODE)) {
isr &= ~DSI_INTR_ERROR;
/* clear only overflow */
mdss_dsi_set_reg(ctrl, 0x0c, 0x44440000, 0x44440000);
@@ -3239,6 +3280,7 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr)
}
if (isr & DSI_INTR_VIDEO_DONE) {
+ MDSS_XLOG(ctrl->ndx, isr, 0x111);
spin_lock(&ctrl->mdp_lock);
mdss_dsi_disable_irq_nosync(ctrl, DSI_VIDEO_TERM);
complete(&ctrl->video_comp);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 6f20c0ed0455..60012c71449c 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -260,16 +260,6 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
rc);
goto rst_gpio_err;
}
- if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) {
- rc = gpio_request(ctrl_pdata->bklt_en_gpio,
- "bklt_enable");
- if (rc) {
- pr_err("request bklt gpio failed, rc=%d\n",
- rc);
- goto bklt_en_gpio_err;
- }
- }
-
if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) {
rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel");
if (rc) {
@@ -282,9 +272,6 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
return rc;
lcd_mode_sel_gpio_err:
- if (gpio_is_valid(ctrl_pdata->bklt_en_gpio))
- gpio_free(ctrl_pdata->bklt_en_gpio);
-bklt_en_gpio_err:
gpio_free(ctrl_pdata->rst_gpio);
rst_gpio_err:
if (gpio_is_valid(ctrl_pdata->disp_en_gpio))
@@ -293,6 +280,81 @@ disp_en_gpio_err:
return rc;
}
+int mdss_dsi_bl_gpio_ctrl(struct mdss_panel_data *pdata, int enable)
+{
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+ int rc = 0, val = 0;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
+ panel_data);
+ if (ctrl_pdata == NULL) {
+ pr_err("%s: Invalid ctrl data\n", __func__);
+ return -EINVAL;
+ }
+
+ /* if gpio is not valid */
+ if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio))
+ return rc;
+
+ pr_debug("%s: enable = %d\n", __func__, enable);
+
+ /*
+ * if gpio state is false and enable (bl level) is
+ * non zero then toggle the gpio
+ */
+ if (!ctrl_pdata->bklt_en_gpio_state && enable) {
+ rc = gpio_request(ctrl_pdata->bklt_en_gpio, "bklt_enable");
+ if (rc) {
+ pr_err("request bklt gpio failed, rc=%d\n", rc);
+ goto free;
+ }
+
+ if (ctrl_pdata->bklt_en_gpio_invert)
+ val = 0;
+ else
+ val = 1;
+
+ rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val);
+ if (rc) {
+ pr_err("%s: unable to set dir for bklt gpio val %d\n",
+ __func__, val);
+ goto free;
+ }
+ ctrl_pdata->bklt_en_gpio_state = true;
+ goto ret;
+ } else if (ctrl_pdata->bklt_en_gpio_state && !enable) {
+ /*
+ * if gpio state is true and enable (bl level) is
+ * zero then toggle the gpio
+ */
+ if (ctrl_pdata->bklt_en_gpio_invert)
+ val = 1;
+ else
+ val = 0;
+
+ rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val);
+ if (rc)
+ pr_err("%s: unable to set dir for bklt gpio val %d\n",
+ __func__, val);
+ goto free;
+ }
+
+ /* gpio state is true and bl level is non zero */
+ goto ret;
+
+free:
+ pr_debug("%s: free bklt gpio\n", __func__);
+ ctrl_pdata->bklt_en_gpio_state = false;
+ gpio_free(ctrl_pdata->bklt_en_gpio);
+ret:
+ return rc;
+}
+
int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
@@ -362,26 +424,6 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
if (pdata->panel_info.rst_seq[++i])
usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000);
}
-
- if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) {
-
- if (ctrl_pdata->bklt_en_gpio_invert) {
- rc = gpio_direction_output(
- ctrl_pdata->bklt_en_gpio, 0);
- gpio_set_value(
- (ctrl_pdata->bklt_en_gpio), 0);
- } else {
- rc = gpio_direction_output(
- ctrl_pdata->bklt_en_gpio, 1);
- gpio_set_value(
- (ctrl_pdata->bklt_en_gpio), 1);
- }
- if (rc) {
- pr_err("%s: unable to set dir for bklt gpio\n",
- __func__);
- goto exit;
- }
- }
}
if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) {
@@ -410,15 +452,6 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
pr_debug("%s: Reset panel done\n", __func__);
}
} else {
- if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) {
-
- if (ctrl_pdata->bklt_en_gpio_invert)
- gpio_set_value((ctrl_pdata->bklt_en_gpio), 1);
- else
- gpio_set_value((ctrl_pdata->bklt_en_gpio), 0);
-
- gpio_free(ctrl_pdata->bklt_en_gpio);
- }
if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) {
gpio_set_value((ctrl_pdata->disp_en_gpio), 0);
gpio_free(ctrl_pdata->disp_en_gpio);
@@ -801,6 +834,9 @@ static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata,
if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0))
bl_level = pdata->panel_info.bl_min;
+ /* enable the backlight gpio if present */
+ mdss_dsi_bl_gpio_ctrl(pdata, bl_level);
+
switch (ctrl_pdata->bklt_ctrl) {
case BL_WLED:
led_trigger_event(bl_led_trigger, bl_level);
@@ -1350,6 +1386,44 @@ static int mdss_dsi_parse_hdr_settings(struct device_node *np,
return 0;
}
+static int mdss_dsi_parse_split_link_settings(struct device_node *np,
+ struct mdss_panel_info *pinfo)
+{
+ u32 tmp;
+ int rc = 0;
+
+ if (!np) {
+ pr_err("%s: device node pointer is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!pinfo) {
+ pr_err("%s: panel info is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pinfo->split_link_enabled = of_property_read_bool(np,
+ "qcom,split-link-enabled");
+
+ if (pinfo->split_link_enabled) {
+ rc = of_property_read_u32(np,
+ "qcom,sublinks-count", &tmp);
+ /* default num of sublink is 1*/
+ pinfo->mipi.num_of_sublinks = (!rc ? tmp : 1);
+
+ rc = of_property_read_u32(np,
+ "qcom,lanes-per-sublink", &tmp);
+ /* default num of lanes per sublink is 1 */
+ pinfo->mipi.lanes_per_sublink = (!rc ? tmp : 1);
+ }
+
+ pr_info("%s: enable %d sublinks-count %d lanes per sublink %d\n",
+ __func__, pinfo->split_link_enabled,
+ pinfo->mipi.num_of_sublinks,
+ pinfo->mipi.lanes_per_sublink);
+ return 0;
+}
+
static int mdss_dsi_parse_dsc_version(struct device_node *np,
struct mdss_panel_timing *timing)
{
@@ -2734,9 +2808,15 @@ static int mdss_panel_parse_dt(struct device_node *np,
pinfo->mipi.data_lane3 = of_property_read_bool(np,
"qcom,mdss-dsi-lane-3-state");
+ /* parse split link properties */
+ rc = mdss_dsi_parse_split_link_settings(np, pinfo);
+ if (rc)
+ return rc;
+
rc = mdss_panel_parse_display_timings(np, &ctrl_pdata->panel_data);
if (rc)
return rc;
+
rc = mdss_dsi_parse_hdr_settings(np, pinfo);
if (rc)
return rc;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c
index 4208c2c43efb..0f24f66dbcc6 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_status.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_status.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -39,6 +39,35 @@ static uint32_t interval = STATUS_CHECK_INTERVAL_MS;
static int32_t dsi_status_disable = DSI_STATUS_CHECK_INIT;
struct dsi_status_data *pstatus_data;
+int mdss_dsi_check_panel_status(struct mdss_dsi_ctrl_pdata *ctrl, void *arg)
+{
+ struct mdss_mdp_ctl *ctl = NULL;
+ struct msm_fb_data_type *mfd = arg;
+ int ret = 0;
+
+ if (!mfd)
+ return -EINVAL;
+
+ ctl = mfd_to_ctl(mfd);
+
+ if (!ctl || !ctrl)
+ return -EINVAL;
+
+ mutex_lock(&ctl->offlock);
+ /*
+ * if check_status method is not defined
+ * then no need to fail this function,
+ * instead return a positive value.
+ */
+ if (ctrl->check_status)
+ ret = ctrl->check_status(ctrl);
+ else
+ ret = 1;
+ mutex_unlock(&ctl->offlock);
+
+ return ret;
+}
+
/*
* check_dsi_ctrl_status() - Reads MFD structure and
* calls platform specific DSI ctrl Status function.
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index fc47de7692e7..698c5633cf6a 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -374,7 +374,8 @@ static int mdss_fb_get_panel_xres(struct mdss_panel_info *pinfo)
xres = pinfo->xres;
if (pdata->next && pdata->next->active)
xres += mdss_fb_get_panel_xres(&pdata->next->panel_info);
-
+ if (pinfo->split_link_enabled)
+ xres = xres * pinfo->mipi.num_of_sublinks;
return xres;
}
@@ -652,7 +653,7 @@ static ssize_t mdss_fb_get_panel_status(struct device *dev,
ret = scnprintf(buf, PAGE_SIZE, "panel_status=%s\n", "suspend");
} else {
panel_status = mdss_fb_send_panel_event(mfd,
- MDSS_EVENT_DSI_PANEL_STATUS, NULL);
+ MDSS_EVENT_DSI_PANEL_STATUS, mfd);
ret = scnprintf(buf, PAGE_SIZE, "panel_status=%s\n",
panel_status > 0 ? "alive" : "dead");
}
@@ -1595,13 +1596,30 @@ static int mdss_fb_resume(struct platform_device *pdev)
static int mdss_fb_pm_suspend(struct device *dev)
{
struct msm_fb_data_type *mfd = dev_get_drvdata(dev);
+ int rc = 0;
if (!mfd)
return -ENODEV;
dev_dbg(dev, "display pm suspend\n");
- return mdss_fb_suspend_sub(mfd);
+ rc = mdss_fb_suspend_sub(mfd);
+
+ /*
+ * Call MDSS footswitch control to ensure GDSC is
+ * off after pm suspend call. There are cases when
+ * mdss runtime call doesn't trigger even when clock
+ * ref count is zero after fb pm suspend.
+ */
+ if (!rc) {
+ if (mfd->mdp.footswitch_ctrl)
+ mfd->mdp.footswitch_ctrl(false);
+ } else {
+ pr_err("fb pm suspend failed, rc: %d\n", rc);
+ }
+
+ return rc;
+
}
static int mdss_fb_pm_resume(struct device *dev)
@@ -1621,6 +1639,9 @@ static int mdss_fb_pm_resume(struct device *dev)
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
+ if (mfd->mdp.footswitch_ctrl)
+ mfd->mdp.footswitch_ctrl(true);
+
return mdss_fb_resume_sub(mfd);
}
#endif
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index f046ff08cbf7..518c24810acd 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -232,6 +232,7 @@ struct msm_mdp_interface {
int (*configure_panel)(struct msm_fb_data_type *mfd, int mode,
int dest_ctrl);
int (*input_event_handler)(struct msm_fb_data_type *mfd);
+ void (*footswitch_ctrl)(bool on);
int (*pp_release_fnc)(struct msm_fb_data_type *mfd);
void *private1;
};
@@ -392,6 +393,12 @@ static inline void mdss_fb_update_notify_update(struct msm_fb_data_type *mfd)
}
}
+/* Function returns true for split link */
+static inline bool is_panel_split_link(struct msm_fb_data_type *mfd)
+{
+ return mfd && mfd->panel_info && mfd->panel_info->split_link_enabled;
+}
+
/* Function returns true for either any kind of dual display */
static inline bool is_panel_split(struct msm_fb_data_type *mfd)
{
diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
index 834726e84bda..2dc9c8f96c5b 100644
--- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c
+++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
@@ -1381,7 +1381,8 @@ int hdcp_1x_authenticate(void *input)
flush_delayed_work(&hdcp->hdcp_auth_work);
- if (!hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ if (!hdcp_1x_state(HDCP_STATE_INACTIVE) &&
+ !hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) {
pr_err("invalid state\n");
return -EINVAL;
}
@@ -1443,7 +1444,6 @@ int hdcp_1x_reauthenticate(void *input)
DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit);
- hdcp->hdcp_state = HDCP_STATE_INACTIVE;
hdcp_1x_authenticate(hdcp);
return ret;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 37c4be6135aa..599f6cb44c63 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -2604,6 +2604,11 @@ void hdmi_edid_set_video_resolution(void *input, u32 resolution, bool reset)
return;
}
+ if (resolution == HDMI_VFRMT_UNKNOWN) {
+ pr_debug("%s: Default video resolution not set\n", __func__);
+ return;
+ }
+
edid_ctrl->video_resolution = resolution;
if (reset) {
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 171f44815430..a645a3495593 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -233,7 +233,6 @@ static struct mdss_mdp_irq mdp_irq_map[] = {
static struct intr_callback *mdp_intr_cb;
-static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on);
static int mdss_mdp_parse_dt(struct platform_device *pdev);
static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev);
static int mdss_mdp_parse_dt_mixer(struct platform_device *pdev);
@@ -5172,7 +5171,7 @@ static void mdss_mdp_notify_idle_pc(struct mdss_data_type *mdata)
* active (but likely in an idle state), the vote for the CX and the batfet
* rails should not be released.
*/
-static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
+void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
{
int ret;
int active_cnt = 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 2fd047edd3e8..a2139f495f52 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -142,6 +142,25 @@
#define BITS_TO_BYTES(x) DIV_ROUND_UP(x, BITS_PER_BYTE)
+#define PP_PROGRAM_PA 0x1
+#define PP_PROGRAM_PCC 0x2
+#define PP_PROGRAM_IGC 0x4
+#define PP_PROGRAM_ARGC 0x8
+#define PP_PROGRAM_HIST 0x10
+#define PP_PROGRAM_DITHER 0x20
+#define PP_PROGRAM_GAMUT 0x40
+#define PP_PROGRAM_PGC 0x100
+#define PP_PROGRAM_PA_DITHER 0x400
+#define PP_PROGRAM_AD 0x800
+
+#define PP_NORMAL_PROGRAM_MASK (PP_PROGRAM_AD | PP_PROGRAM_PCC | \
+ PP_PROGRAM_HIST)
+#define PP_DEFER_PROGRAM_MASK (PP_PROGRAM_IGC | PP_PROGRAM_PGC | \
+ PP_PROGRAM_ARGC | PP_PROGRAM_GAMUT | \
+ PP_PROGRAM_PA | PP_PROGRAM_DITHER | \
+ PP_PROGRAM_PA_DITHER)
+#define PP_PROGRAM_ALL (PP_NORMAL_PROGRAM_MASK | PP_DEFER_PROGRAM_MASK)
+
enum mdss_mdp_perf_state_type {
PERF_SW_COMMIT_STATE = 0,
PERF_HW_MDP_STATE,
@@ -773,6 +792,12 @@ struct mdss_pipe_pp_res {
void *hist_lut_cfg_payload;
};
+struct mdss_mdp_pp_program_info {
+ u32 pp_program_mask;
+ u32 pp_opmode_left;
+ u32 pp_opmode_right;
+};
+
struct mdss_mdp_pipe_smp_map {
DECLARE_BITMAP(reserved, MAX_DRV_SUP_MMB_BLKS);
DECLARE_BITMAP(allocated, MAX_DRV_SUP_MMB_BLKS);
@@ -1267,6 +1292,8 @@ static inline u32 get_panel_width(struct mdss_mdp_ctl *ctl)
width = get_panel_xres(&ctl->panel_data->panel_info);
if (ctl->panel_data->next && is_pingpong_split(ctl->mfd))
width += get_panel_xres(&ctl->panel_data->next->panel_info);
+ else if (is_panel_split_link(ctl->mfd))
+ width *= (ctl->panel_data->panel_info.mipi.num_of_sublinks);
return width;
}
@@ -1638,6 +1665,7 @@ int mdss_mdp_set_intr_callback_nosync(u32 intr_type, u32 intf_num,
void (*fnc_ptr)(void *), void *arg);
u32 mdss_mdp_get_irq_mask(u32 intr_type, u32 intf_num);
+void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on);
void mdss_mdp_footswitch_ctrl_splash(int on);
void mdss_mdp_batfet_ctrl(struct mdss_data_type *mdata, int enable);
void mdss_mdp_set_clk_rate(unsigned long min_clk_rate, bool locked);
@@ -1800,7 +1828,8 @@ int mdss_mdp_pp_resume(struct msm_fb_data_type *mfd);
void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl);
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl);
-int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_pp_program_info *info);
int mdss_mdp_pipe_pp_setup(struct mdss_mdp_pipe *pipe, u32 *op);
void mdss_mdp_pipe_pp_clear(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_cdm.c b/drivers/video/fbdev/msm/mdss_mdp_cdm.c
index f1d1bdd301e3..10928e6bceaa 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_cdm.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_cdm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -377,6 +377,17 @@ int mdss_mdp_cdm_destroy(struct mdss_mdp_cdm *cdm)
return -EINVAL;
}
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
+ mutex_lock(&cdm->lock);
+ /* Disable HDMI packer */
+ writel_relaxed(0x0, cdm->base + MDSS_MDP_REG_CDM_HDMI_PACK_OP_MODE);
+
+ /* Put CDM in bypass */
+ writel_relaxed(0x0, cdm->mdata->mdp_base + MDSS_MDP_MDP_OUT_CTL_0);
+
+ mutex_unlock(&cdm->lock);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
+
kref_put(&cdm->kref, mdss_mdp_cdm_free);
return rc;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 49348e5e16a9..1f29a9f86e24 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -709,6 +709,8 @@ int mdss_mdp_get_panel_params(struct mdss_mdp_pipe *pipe,
*h_total += mdss_panel_get_htotal(
&mixer->ctl->panel_data->next->panel_info,
false);
+ else if (is_panel_split_link(mixer->ctl->mfd))
+ *h_total *= pinfo->mipi.num_of_sublinks;
} else {
*v_total = mixer->height;
*xres = mixer->width;
@@ -4090,6 +4092,13 @@ static void mdss_mdp_ctl_split_display_enable(int enable,
if (is_pingpong_split(main_ctl->mfd))
lower |= BIT(2);
upper = lower;
+
+ /*
+ * align command mode stream0 output for
+ * intferace 1 and 2 to start of frame.
+ */
+ if (main_ctl->mdata->mdp_rev >= MDSS_MDP_HW_REV_320)
+ lower |= BIT(12);
} else {
/* interface controlling sw trigger (video mode) */
if (main_ctl->intf_num == MDSS_MDP_INTF2) {
@@ -4101,6 +4110,9 @@ static void mdss_mdp_ctl_split_display_enable(int enable,
}
}
}
+
+ if (is_panel_split_link(main_ctl->mfd))
+ upper = lower = 0;
writel_relaxed(upper, main_ctl->mdata->mdp_base +
MDSS_MDP_REG_SPLIT_DISPLAY_UPPER_PIPE_CTRL);
writel_relaxed(lower, main_ctl->mdata->mdp_base +
@@ -4269,7 +4281,8 @@ void mdss_mdp_ctl_restore(bool locked)
if (sctl) {
mdss_mdp_ctl_restore_sub(sctl);
mdss_mdp_ctl_split_display_enable(1, ctl, sctl);
- } else if (is_pingpong_split(ctl->mfd)) {
+ } else if (is_pingpong_split(ctl->mfd) ||
+ is_panel_split_link(ctl->mfd)) {
mdss_mdp_ctl_pp_split_display_enable(1, ctl);
}
@@ -4396,6 +4409,8 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl, bool handoff)
} else if (is_pingpong_split(ctl->mfd)) {
ctl->slave_intf_num = (ctl->intf_num + 1);
mdss_mdp_ctl_pp_split_display_enable(true, ctl);
+ } else if (is_panel_split_link(ctl->mfd)) {
+ mdss_mdp_ctl_pp_split_display_enable(true, ctl);
}
}
@@ -4570,7 +4585,7 @@ void mdss_mdp_check_ctl_reset_status(struct mdss_mdp_ctl *ctl)
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");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
}
}
@@ -5737,6 +5752,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
bool is_bw_released, split_lm_valid;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 ctl_flush_bits = 0, sctl_flush_bits = 0;
+ /* Must initialize pp_program_info */
+ struct mdss_mdp_pp_program_info pp_program_info = {
+ PP_PROGRAM_ALL, 0, 0};
if (!ctl) {
pr_err("display function not set\n");
@@ -5849,9 +5867,13 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
mdss_mdp_ctl_split_display_enable(split_lm_valid, ctl, sctl);
ATRACE_BEGIN("postproc_programming");
- if (ctl->is_video_mode && ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER)
+ if (ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER) {
/* postprocessing setup, including dspp */
- mdss_mdp_pp_setup_locked(ctl);
+ if (!ctl->is_video_mode)
+ pp_program_info.pp_program_mask =
+ PP_NORMAL_PROGRAM_MASK;
+ mdss_mdp_pp_setup_locked(ctl, &pp_program_info);
+ }
if (sctl) {
if (ctl->split_flush_en) {
@@ -5907,11 +5929,17 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
}
/* Moved pp programming to post ping pong */
+ ATRACE_BEGIN("postproc_programming_deferred");
if (!ctl->is_video_mode && ctl->mfd &&
ctl->mfd->dcm_state != DTM_ENTER) {
/* postprocessing setup, including dspp */
mutex_lock(&ctl->flush_lock);
- mdss_mdp_pp_setup_locked(ctl);
+ pp_program_info.pp_program_mask = PP_DEFER_PROGRAM_MASK;
+ /*
+ * pp_program_info should not be modified beween normal and
+ * deferred stage calls.
+ */
+ mdss_mdp_pp_setup_locked(ctl, &pp_program_info);
if (sctl) {
if (ctl->split_flush_en) {
ctl->flush_bits |= sctl->flush_bits;
@@ -5924,6 +5952,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
ctl_flush_bits |= ctl->flush_bits;
mutex_unlock(&ctl->flush_lock);
}
+ ATRACE_END("postproc_programming_deferred");
/*
* if serialize_wait4pp is false then roi_bkup used in wait4pingpong
* will be of previous frame as expected.
@@ -6056,8 +6085,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
/* update backlight in commit */
if (mdss_mdp_handle_backlight_extn(ctl)) {
- if (!IS_CALIB_MODE_BL(ctl->mfd) && (!ctl->mfd->ext_bl_ctrl ||
- !ctl->mfd->bl_level)) {
+ if (ctl->mfd && !IS_CALIB_MODE_BL(ctl->mfd) &&
+ (!ctl->mfd->ext_bl_ctrl ||
+ !ctl->mfd->bl_level)) {
mutex_lock(&ctl->mfd->bl_lock);
mdss_fb_set_backlight(ctl->mfd,
ctl->mfd->bl_extn_level);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 7d495232c198..d9e2b042bfc3 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -850,4 +850,8 @@ enum mdss_mdp_pingpong_index {
#define MDSS_MDP_REG_TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4))
#define MDSS_MDP_REG_TRAFFIC_SHAPER_FIXPOINT_FACTOR 4
+#define MDSS_MDP_REG_SPLIT_LINK 0x00060
+#define MDSS_MDP_REG_SPLIT_LINK_LEFT_LINK_EN BIT(1)
+#define MDSS_MDP_REG_SPLIT_LINK_RIGHT_LINK_EN BIT(2)
+
#endif
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index c9ce56fb96a4..bdbec0b4e721 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -2148,12 +2148,14 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg)
MDSS_XLOG(0xbad);
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt",
- "dbg_bus", "vbif_dbg_bus", "panic");
+ "dbg_bus", "vbif_dbg_bus",
+ "dsi_dbg_bus", "panic");
} else if (ctx->pp_timeout_report_cnt == MAX_RECOVERY_TRIALS) {
MDSS_XLOG(0xbad2);
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt",
- "dbg_bus", "vbif_dbg_bus", "panic");
+ "dbg_bus", "vbif_dbg_bus",
+ "dsi_dbg_bus", "panic");
mdss_fb_report_panel_dead(ctl->mfd);
}
ctx->pp_timeout_report_cnt++;
@@ -3352,8 +3354,18 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
* mode.
*/
send_panel_events = true;
- if (mdss_panel_is_power_on_ulp(panel_power_state))
+ if (mdss_panel_is_power_on_ulp(panel_power_state)) {
turn_off_clocks = true;
+ } else if (atomic_read(&ctx->koff_cnt)) {
+ /*
+ * Transition from interactive to low power
+ * Wait for kickoffs to finish
+ */
+ MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt));
+ mdss_mdp_cmd_wait4pingpong(ctl, NULL);
+ if (sctl)
+ mdss_mdp_cmd_wait4pingpong(sctl, NULL);
+ }
} else {
/* Transitions between low power and ultra low power */
if (mdss_panel_is_power_on_ulp(panel_power_state)) {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index a3511a1a07ef..13c70822e266 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -1071,6 +1071,13 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
{
int intfs_num, ret = 0;
+ if (ctl->cdm) {
+ if (!mdss_mdp_cdm_destroy(ctl->cdm))
+ mdss_mdp_ctl_write(ctl,
+ MDSS_MDP_REG_CTL_FLUSH, BIT(26));
+ ctl->cdm = NULL;
+ }
+
intfs_num = ctl->intf_num - MDSS_MDP_INTF0;
ret = mdss_mdp_video_intfs_stop(ctl, ctl->panel_data, intfs_num);
if (IS_ERR_VALUE(ret)) {
@@ -1083,10 +1090,6 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
mdss_mdp_ctl_reset(ctl, false);
ctl->intf_ctx[MASTER_CTX] = NULL;
- if (ctl->cdm) {
- mdss_mdp_cdm_destroy(ctl->cdm);
- ctl->cdm = NULL;
- }
return 0;
}
@@ -1384,7 +1387,7 @@ static int mdss_mdp_video_dfps_wait4vsync(struct mdss_mdp_ctl *ctl)
pr_err("error polling for vsync\n");
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
}
} else {
rc = 0;
@@ -1682,6 +1685,16 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
mdss_bus_bandwidth_ctrl(true);
+ /* configure the split link to both sublinks */
+ if (is_panel_split_link(ctl->mfd)) {
+ mdp_video_write(ctx, MDSS_MDP_REG_SPLIT_LINK, 0x3);
+ /*
+ * ensure split link register is written before
+ * enabling timegen
+ */
+ wmb();
+ }
+
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
wmb();
@@ -1796,7 +1809,9 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
mdss_mdp_video_timegen_flush(ctl, sctx);
/* wait for 1 VSYNC for the pipe to be unstaged */
- msleep(20);
+ mdss_mdp_video_wait4comp(ctl, NULL);
+ if (sctl)
+ mdss_mdp_video_wait4comp(sctl, NULL);
ret = mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_CONT_SPLASH_FINISH, NULL,
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
index 87ed56028edd..9b63499e64b0 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
@@ -792,7 +792,9 @@ static int mdss_mdp_writeback_stop(struct mdss_mdp_ctl *ctl,
}
if (ctl->cdm) {
- mdss_mdp_cdm_destroy(ctl->cdm);
+ if (!mdss_mdp_cdm_destroy(ctl->cdm))
+ mdss_mdp_ctl_write(ctl,
+ MDSS_MDP_REG_CTL_FLUSH, BIT(26));
ctl->cdm = NULL;
}
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 8eb12d764be3..8c612e2b83fb 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -2601,6 +2601,18 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
mdss_mdp_splash_cleanup(mfd, true);
/*
+ * Wait for pingpong done only during resume for
+ * command mode panels. Ensure that one commit is
+ * sent before kickoff completes so that backlight
+ * update happens after it.
+ */
+ if (mdss_fb_is_power_off(mfd) &&
+ mfd->panel_info->type == MIPI_CMD_PANEL) {
+ pr_debug("wait for pp done after resume for cmd mode\n");
+ mdss_mdp_display_wait4pingpong(ctl, true);
+ }
+
+ /*
* Configure Timing Engine, if new fps was set.
* We need to do this after the wait for vsync
* to guarantee that mdp flush bit and dsi flush
@@ -6279,6 +6291,13 @@ int mdss_mdp_input_event_handler(struct msm_fb_data_type *mfd)
return rc;
}
+void mdss_mdp_footswitch_ctrl_handler(bool on)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ mdss_mdp_footswitch_ctrl(mdata, on);
+}
+
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
struct device *dev = mfd->fbi->dev;
@@ -6321,6 +6340,14 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
mdp5_interface->configure_panel = mdss_mdp_update_panel_info;
mdp5_interface->input_event_handler = mdss_mdp_input_event_handler;
+ /*
+ * Register footswitch control only for primary fb pm
+ * suspend/resume calls.
+ */
+ if (mfd->panel_info->is_prim_panel)
+ mdp5_interface->footswitch_ctrl =
+ mdss_mdp_footswitch_ctrl_handler;
+
if (mfd->panel_info->type == WRITEBACK_PANEL) {
mdp5_interface->atomic_validate =
mdss_mdp_layer_atomic_validate_wfd;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
index 8d7bd60318ad..724913f376a7 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
@@ -2292,6 +2292,9 @@ static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe,
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC3_ADDR, addr[2]);
}
+ MDSS_XLOG(pipe->num, pipe->multirect.num, pipe->mixer_left->num,
+ pipe->play_cnt, addr[0], addr[1], addr[2], addr[3]);
+
return 0;
}
@@ -2734,9 +2737,6 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
goto update_nobuf;
}
- MDSS_XLOG(pipe->num, pipe->multirect.num, pipe->mixer_left->num,
- pipe->play_cnt, 0x222);
-
if (params_changed) {
pipe->params_changed = 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index f10d4fb60f52..f128f82fab04 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -2322,7 +2322,9 @@ static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num,
*opmode |= MDSS_MDP_DSPP_OP_ARGC_LUT_EN;
}
-static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
+static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer,
+ u32 pp_program_mask, int *op_mode)
+
{
u32 ad_flags, flags, dspp_num, opmode = 0, ad_bypass;
struct mdp_pgc_lut_data *pgc_config;
@@ -2338,6 +2340,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
int side;
+ opmode = *op_mode;
+
if (!mixer || !mixer->ctl || !mixer->ctl->mdata)
return -EINVAL;
ctl = mixer->ctl;
@@ -2358,19 +2362,23 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
- if ((mdata->pp_block_off.dspp_gamut_off != U32_MAX) &&
- (pp_driver_ops.gamut_clk_gate_en))
- pp_driver_ops.gamut_clk_gate_en(base +
+ if (pp_program_mask & PP_PROGRAM_GAMUT) {
+ if ((mdata->pp_block_off.dspp_gamut_off != U32_MAX) &&
+ (pp_driver_ops.gamut_clk_gate_en))
+ pp_driver_ops.gamut_clk_gate_en(base +
mdata->pp_block_off.dspp_gamut_off);
-
+ }
if (disp_num < MDSS_BLOCK_DISP_NUM) {
pp_sts = &mdss_pp_res->pp_disp_sts[disp_num];
pp_sts->side_sts = side;
- ret = pp_hist_setup(&opmode, MDSS_PP_DSPP_CFG | dspp_num, mixer,
- pp_sts);
- if (ret)
- goto dspp_exit;
+ if (pp_program_mask & PP_PROGRAM_HIST) {
+ ret = pp_hist_setup(&opmode,
+ MDSS_PP_DSPP_CFG | dspp_num, mixer,
+ pp_sts);
+ if (ret)
+ goto dspp_exit;
+ }
flags = mdss_pp_res->pp_disp_flags[disp_num];
} else {
@@ -2391,7 +2399,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
if ((!flags) && (!(opmode)) && (!ad_flags))
goto dspp_exit;
- if (flags & PP_FLAGS_DIRTY_PA) {
+ if ((flags & PP_FLAGS_DIRTY_PA) &&
+ (pp_program_mask & PP_PROGRAM_PA)) {
if (!pp_ops[PA].pp_set_config) {
if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) {
pa_v2_cfg_data =
@@ -2412,7 +2421,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
DSPP);
}
}
- if (flags & PP_FLAGS_DIRTY_PCC) {
+ if ((flags & PP_FLAGS_DIRTY_PCC) &&
+ (pp_program_mask & PP_PROGRAM_PCC)) {
if (!pp_ops[PCC].pp_set_config)
pp_pcc_config(flags, base + MDSS_MDP_REG_DSPP_PCC_BASE,
pp_sts,
@@ -2429,7 +2439,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
}
- if (flags & PP_FLAGS_DIRTY_IGC) {
+ if ((flags & PP_FLAGS_DIRTY_IGC) &&
+ (pp_program_mask & PP_PROGRAM_IGC)) {
if (!pp_ops[IGC].pp_set_config) {
pp_igc_config(flags,
mdata->mdp_base + MDSS_MDP_REG_IGC_DSPP_BASE,
@@ -2449,7 +2460,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
DSPP);
}
- if (flags & PP_FLAGS_DIRTY_ENHIST) {
+ if ((flags & PP_FLAGS_DIRTY_ENHIST) &&
+ (pp_program_mask & PP_PROGRAM_HIST)) {
if (!pp_ops[HIST_LUT].pp_set_config) {
pp_enhist_config(flags,
base + MDSS_MDP_REG_DSPP_HIST_LUT_BASE,
@@ -2473,7 +2485,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
}
- if (flags & PP_FLAGS_DIRTY_DITHER) {
+ if ((flags & PP_FLAGS_DIRTY_DITHER) &&
+ (pp_program_mask & PP_PROGRAM_DITHER)) {
if (!pp_ops[DITHER].pp_set_config && addr) {
pp_dither_config(addr, pp_sts,
&mdss_pp_res->dither_disp_cfg[disp_num]);
@@ -2483,7 +2496,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
&mdss_pp_res->dither_disp_cfg[disp_num], DSPP);
}
}
- if (flags & PP_FLAGS_DIRTY_GAMUT) {
+ if ((flags & PP_FLAGS_DIRTY_GAMUT) &&
+ (pp_program_mask & PP_PROGRAM_GAMUT)) {
if (!pp_ops[GAMUT].pp_set_config) {
pp_gamut_config(&mdss_pp_res->gamut_disp_cfg[disp_num],
base, pp_sts);
@@ -2500,7 +2514,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
}
- if (flags & PP_FLAGS_DIRTY_PGC) {
+ if ((flags & PP_FLAGS_DIRTY_PGC) &&
+ (pp_program_mask & PP_PROGRAM_PGC)) {
pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num];
if (pp_ops[GC].pp_set_config) {
if (mdata->pp_block_off.dspp_pgc_off == U32_MAX) {
@@ -2526,6 +2541,7 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
}
if (flags & PP_FLAGS_DIRTY_PA_DITHER &&
+ (pp_program_mask & PP_PROGRAM_PA_DITHER) &&
pp_ops[PA_DITHER].pp_set_config) {
pp_ops[PA_DITHER].pp_set_config(base, pp_sts,
&mdss_pp_res->pa_dither_cfg[disp_num],
@@ -2536,7 +2552,7 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
pp_dspp_opmode_config(ctl, dspp_num, pp_sts, mdata->mdp_rev,
&opmode);
- if (ad_hw) {
+ if (ad_hw && (pp_program_mask & PP_PROGRAM_AD)) {
mutex_lock(&ad->lock);
ad_flags = ad->reg_sts;
if (ad_flags & PP_AD_STS_DIRTY_DATA)
@@ -2566,6 +2582,9 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
ctl->flush_bits |= BIT(13 + dspp_num);
wmb();
+
+ *op_mode = opmode;
+
dspp_exit:
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
return ret;
@@ -2684,6 +2703,8 @@ void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl)
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
{
int ret = 0;
+ struct mdss_mdp_pp_program_info pp_program_info = {
+ PP_PROGRAM_ALL, 0, 0};
if ((!ctl->mfd) || (!mdss_pp_res))
return -EINVAL;
@@ -2695,14 +2716,15 @@ int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
ret = -EPERM;
goto error;
}
- ret = mdss_mdp_pp_setup_locked(ctl);
+ ret = mdss_mdp_pp_setup_locked(ctl, &pp_program_info);
error:
mutex_unlock(&ctl->lock);
return ret;
}
-int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
+int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_pp_program_info *info)
{
struct mdss_data_type *mdata;
int ret = 0, i;
@@ -2715,6 +2737,16 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
bool valid_ad_panel = true;
if ((!ctl) || (!ctl->mfd) || (!mdss_pp_res) || (!ctl->mdata))
return -EINVAL;
+ if (!info) {
+ pr_err("pp_program_info is NULL");
+ return -EINVAL;
+ }
+ if (!(info->pp_program_mask == PP_NORMAL_PROGRAM_MASK ||
+ info->pp_program_mask == PP_DEFER_PROGRAM_MASK ||
+ info->pp_program_mask == PP_PROGRAM_ALL)) {
+ pr_err("Invalid pp program mask : %x ", info->pp_program_mask);
+ return -EINVAL;
+ }
mdata = ctl->mdata;
/* treat fb_num the same as block logical id*/
@@ -2748,7 +2780,11 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
mutex_lock(&mdss_pp_mutex);
- flags = mdss_pp_res->pp_disp_flags[disp_num];
+ if (disp_num < MDSS_BLOCK_DISP_NUM)
+ flags = mdss_pp_res->pp_disp_flags[disp_num];
+ else
+ flags = 0;
+
if (pp_ops[PA].pp_set_config)
pa_v2_flags = mdss_pp_res->pa_v2_disp_cfg[disp_num].flags;
else
@@ -2759,50 +2795,72 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
* increase the register bus bandwidth to maximum frequency
* in order to speed up the register reprogramming.
*/
- max_bw_needed = (IS_PP_RESUME_COMMIT(flags) &&
- (IS_PP_LUT_DIRTY(flags) ||
- IS_SIX_ZONE_DIRTY(flags, pa_v2_flags)));
- if (mdata->pp_reg_bus_clt && max_bw_needed) {
- ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
- VOTE_INDEX_HIGH);
- if (ret)
- pr_err("Updated reg_bus_scale failed, ret = %d", ret);
+ if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) {
+ max_bw_needed = (IS_PP_RESUME_COMMIT(flags) &&
+ (IS_PP_LUT_DIRTY(flags) ||
+ IS_SIX_ZONE_DIRTY(flags, pa_v2_flags)));
+ if (mdata->pp_reg_bus_clt && max_bw_needed) {
+ ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
+ VOTE_INDEX_HIGH);
+ if (ret)
+ pr_err("Updated reg_bus_scale failed, ret = %d",
+ ret);
+ }
}
if (ctl->mixer_left) {
- pp_mixer_setup(ctl->mixer_left);
- pp_dspp_setup(disp_num, ctl->mixer_left);
- pp_ppb_setup(ctl->mixer_left);
+ if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) {
+ pp_mixer_setup(ctl->mixer_left);
+ pp_dspp_setup(disp_num, ctl->mixer_left,
+ info->pp_program_mask, &info->pp_opmode_left);
+ pp_ppb_setup(ctl->mixer_left);
+ } else {
+ pp_dspp_setup(disp_num, ctl->mixer_left,
+ info->pp_program_mask, &info->pp_opmode_left);
+ }
}
if (ctl->mixer_right) {
- pp_mixer_setup(ctl->mixer_right);
- pp_dspp_setup(disp_num, ctl->mixer_right);
- pp_ppb_setup(ctl->mixer_right);
+ if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) {
+ pp_mixer_setup(ctl->mixer_right);
+ pp_dspp_setup(disp_num, ctl->mixer_right,
+ info->pp_program_mask, &info->pp_opmode_right);
+ pp_ppb_setup(ctl->mixer_right);
+ } else {
+ pp_dspp_setup(disp_num, ctl->mixer_right,
+ info->pp_program_mask, &info->pp_opmode_right);
+ }
}
- if (valid_mixers && (mixer_cnt <= mdata->nmax_concurrent_ad_hw) &&
- valid_ad_panel) {
- ret = mdss_mdp_ad_ipc_reset(ctl->mfd);
- if (ret < 0)
- pr_warn("ad_setup(disp%d) returns %d\n", disp_num, ret);
+ if (info->pp_program_mask & PP_PROGRAM_AD) {
+ if (valid_mixers &&
+ (mixer_cnt <= mdata->nmax_concurrent_ad_hw) &&
+ valid_ad_panel) {
+ ret = mdss_mdp_ad_ipc_reset(ctl->mfd);
+ if (ret < 0)
+ pr_warn("ad_setup(disp%d) returns %d\n",
+ disp_num, ret);
+ }
}
- /* clear dirty flag */
- if (disp_num < MDSS_BLOCK_DISP_NUM) {
- mdss_pp_res->pp_disp_flags[disp_num] = 0;
- if (disp_num < mdata->nad_cfgs)
- mdata->ad_cfgs[disp_num].reg_sts = 0;
- }
+ if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) {
+ /* clear dirty flag */
+ if (disp_num < MDSS_BLOCK_DISP_NUM) {
+ mdss_pp_res->pp_disp_flags[disp_num] = 0;
+ if (disp_num < mdata->nad_cfgs)
+ mdata->ad_cfgs[disp_num].reg_sts = 0;
+ }
- if (mdata->pp_reg_bus_clt && max_bw_needed) {
- ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
- VOTE_INDEX_DISABLE);
- if (ret)
- pr_err("Updated reg_bus_scale failed, ret = %d", ret);
+ if (mdata->pp_reg_bus_clt && max_bw_needed) {
+ ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
+ VOTE_INDEX_DISABLE);
+ if (ret)
+ pr_err("Updated reg_bus_scale failed, ret = %d",
+ ret);
+ }
+ if (IS_PP_RESUME_COMMIT(flags))
+ mdss_pp_res->pp_disp_flags[disp_num] &=
+ ~PP_FLAGS_RESUME_COMMIT;
}
- if (IS_PP_RESUME_COMMIT(flags))
- mdss_pp_res->pp_disp_flags[disp_num] &=
- ~PP_FLAGS_RESUME_COMMIT;
mutex_unlock(&mdss_pp_mutex);
exit:
return ret;
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index 92413e078244..fa1df94976f9 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -533,6 +533,8 @@ struct mipi_panel_info {
char lp11_init;
u32 init_delay;
u32 post_init_delay;
+ u32 num_of_sublinks;
+ u32 lanes_per_sublink;
};
struct edp_panel_info {
@@ -847,6 +849,7 @@ struct mdss_panel_info {
bool is_lpm_mode;
bool is_split_display; /* two DSIs in one display, pp split or not */
bool use_pingpong_split;
+ bool split_link_enabled;
/*
* index[0] = left layer mixer, value of 0 not valid
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 62e25500060e..1b4765837c61 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -600,24 +600,30 @@ int mdss_smmu_fault_handler(struct iommu_domain *domain, struct device *dev,
(struct mdss_smmu_client *)user_data;
u32 fsynr1, mid, i;
- if (!mdss_smmu || !mdss_smmu->mmu_base)
+ if (!mdss_smmu)
goto end;
- fsynr1 = readl_relaxed(mdss_smmu->mmu_base + SMMU_CBN_FSYNR1);
- mid = fsynr1 & 0xff;
- pr_err("mdss_smmu: iova:0x%lx flags:0x%x fsynr1: 0x%x mid: 0x%x\n",
- iova, flags, fsynr1, mid);
+ if (mdss_smmu->mmu_base) {
+ fsynr1 = readl_relaxed(mdss_smmu->mmu_base + SMMU_CBN_FSYNR1);
+ mid = fsynr1 & 0xff;
+ pr_err("mdss_smmu: iova:0x%lx flags:0x%x fsynr1: 0x%x mid: 0x%x\n",
+ iova, flags, fsynr1, mid);
- /* get domain id information */
- for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) {
- if (mdss_smmu == mdss_smmu_get_cb(i))
- break;
- }
+ /* get domain id information */
+ for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) {
+ if (mdss_smmu == mdss_smmu_get_cb(i))
+ break;
+ }
- if (i == MDSS_IOMMU_MAX_DOMAIN)
- goto end;
+ if (i == MDSS_IOMMU_MAX_DOMAIN)
+ goto end;
- mdss_mdp_debug_mid(mid);
+ mdss_mdp_debug_mid(mid);
+ } else {
+ pr_err("mdss_smmu: iova:0x%lx flags:0x%x\n",
+ iova, flags);
+ MDSS_XLOG_TOUT_HANDLER("mdp");
+ }
end:
return -ENOSYS;
}
@@ -694,13 +700,13 @@ int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev)
}
static struct mdss_smmu_domain mdss_mdp_unsec = {
- "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)};
+ "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_4G - SZ_128M)};
static struct mdss_smmu_domain mdss_rot_unsec = {
- NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)};
+ NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_4G - SZ_128M)};
static struct mdss_smmu_domain mdss_mdp_sec = {
- "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_128K, (SZ_4G - SZ_128K)};
+ "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_128K, (SZ_4G - SZ_128M)};
static struct mdss_smmu_domain mdss_rot_sec = {
- NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_128K, (SZ_4G - SZ_128K)};
+ NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_128K, (SZ_4G - SZ_128M)};
static const struct of_device_id mdss_smmu_dt_match[] = {
{ .compatible = "qcom,smmu_mdp_unsec", .data = &mdss_mdp_unsec},
@@ -844,14 +850,13 @@ int mdss_smmu_probe(struct platform_device *pdev)
mdss_smmu->base.dev = dev;
+ iommu_set_fault_handler(mdss_smmu->mmu_mapping->domain,
+ mdss_smmu_fault_handler, mdss_smmu);
address = of_get_address_by_name(pdev->dev.of_node, "mmu_cb", 0, 0);
if (address) {
size = address + 1;
mdss_smmu->mmu_base = ioremap(be32_to_cpu(*address),
be32_to_cpu(*size));
- if (mdss_smmu->mmu_base)
- iommu_set_fault_handler(mdss_smmu->mmu_mapping->domain,
- mdss_smmu_fault_handler, mdss_smmu);
} else {
pr_debug("unable to map context bank base\n");
}
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index 03e78733d168..c81d45b57f8d 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -38,16 +38,23 @@
#define MDSS_DSI_DSIPHY_GLBL_TEST_CTRL 0x1d4
#define MDSS_DSI_DSIPHY_CTRL_0 0x170
#define MDSS_DSI_DSIPHY_CTRL_1 0x174
+#define MDSS_DSI_DSIPHY_CMN_CLK_CFG0 0x0010
+#define MDSS_DSI_DSIPHY_CMN_CLK_CFG1 0x0014
+
+#define MDSS_DSI_NUM_DATA_LANES 0x04
+#define MDSS_DSI_NUM_CLK_LANES 0x01
#define SW_RESET BIT(2)
#define SW_RESET_PLL BIT(0)
#define PWRDN_B BIT(7)
/* 8996 */
-#define DATALANE_OFFSET_FROM_BASE_8996 0x100
-#define DSIPHY_CMN_PLL_CNTRL 0x0048
+#define DATALANE_OFFSET_FROM_BASE_8996 0x100
+#define CLKLANE_OFFSET_FROM_BASE_8996 0x300
#define DATALANE_SIZE_8996 0x80
+#define CLKLANE_SIZE_8996 0x80
+#define DSIPHY_CMN_PLL_CNTRL 0x0048
#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018
#define DSIPHY_CMN_CTRL_0 0x001c
#define DSIPHY_CMN_CTRL_1 0x0020
@@ -55,6 +62,24 @@
#define DSIPHY_PLL_CLKBUFLR_EN 0x041c
#define DSIPHY_PLL_PLL_BANDGAP 0x0508
+#define DSIPHY_LANE_STRENGTH_CTRL_NUM 0x0002
+#define DSIPHY_LANE_STRENGTH_CTRL_OFFSET 0x0004
+#define DSIPHY_LANE_STRENGTH_CTRL_BASE 0x0038
+
+#define DSIPHY_LANE_CFG_NUM 0x0004
+#define DSIPHY_LANE_CFG_OFFSET 0x0004
+#define DSIPHY_LANE_CFG_BASE 0x0000
+
+#define DSIPHY_LANE_VREG_NUM 0x0001
+#define DSIPHY_LANE_VREG_OFFSET 0x0004
+#define DSIPHY_LANE_VREG_BASE 0x0064
+
+#define DSIPHY_LANE_TIMING_CTRL_NUM 0x0008
+#define DSIPHY_LANE_TIMING_CTRL_OFFSET 0x0004
+#define DSIPHY_LANE_TIMING_CTRL_BASE 0x0018
+
+#define DSIPHY_LANE_TEST_STR 0x0014
+
#define DSIPHY_LANE_STRENGTH_CTRL_1 0x003c
#define DSIPHY_LANE_VREG_CNTRL 0x0064
@@ -131,6 +156,8 @@
#define DSIPHY_PLL_RESETSM_CNTRL5 0x043c
+#define DSIPHY_CMN_CLK_CFG1_SPLIT_LINK 0x1
+
#define PLL_CALC_DATA(addr0, addr1, data0, data1) \
(((data1) << 24) | ((((addr1)/4) & 0xFF) << 16) | \
((data0) << 8) | (((addr0)/4) & 0xFF))
@@ -911,35 +938,59 @@ static void mdss_dsi_8996_phy_regulator_enable(
int j, off, ln, cnt, ln_off;
char *ip;
void __iomem *base;
+ struct mdss_panel_info *panel_info;
+ if (!ctrl) {
+ pr_warn("%s: null ctrl pdata\n", __func__);
+ return;
+ }
+
+ panel_info = &((ctrl->panel_data).panel_info);
pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db);
- if (pd->regulator_len != 5) {
+ if (pd->regulator_len != (MDSS_DSI_NUM_DATA_LANES +
+ MDSS_DSI_NUM_CLK_LANES)) {
pr_warn("%s: invalid regulator settings\n", __func__);
return;
}
- /* 4 lanes + clk lane configuration */
- for (ln = 0; ln < 5; ln++) {
- /*
- * data lane offset frome base: 0x100
- * data lane size: 0x80
- */
- base = ctrl->phy_io.base +
- DATALANE_OFFSET_FROM_BASE_8996;
- base += (ln * DATALANE_SIZE_8996); /* lane base */
-
- /* vreg ctrl, 1 * 5 */
- cnt = 1;
+ /*
+ * data lane offset from base: 0x100
+ * data lane size: 0x80
+ */
+ base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996;
+ /* data lanes configuration */
+ for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) {
+ /* vreg ctrl, 1 * MDSS_DSI_NUM_DATA_LANES */
+ cnt = DSIPHY_LANE_VREG_NUM;
+ off = DSIPHY_LANE_VREG_BASE;
ln_off = cnt * ln;
ip = &pd->regulator[ln_off];
- off = 0x64;
- for (j = 0; j < cnt; j++, off += 4)
+ for (j = 0; j < cnt; j++) {
MIPI_OUTP(base + off, *ip++);
+ off += DSIPHY_LANE_VREG_OFFSET;
+ }
+ base += DATALANE_SIZE_8996; /* next lane */
}
- wmb(); /* make sure registers committed */
+ /*
+ * clk lane offset from base: 0x300
+ * clk lane size: 0x80
+ */
+ base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996;
+ /*
+ * clk lane configuration for vreg ctrl
+ * for split link there are two clock lanes, one
+ * clock lane per sublink needs to be configured
+ */
+ off = DSIPHY_LANE_VREG_BASE;
+ ln_off = MDSS_DSI_NUM_DATA_LANES;
+ ip = &pd->regulator[ln_off];
+ MIPI_OUTP(base + off, *ip);
+ if (panel_info->split_link_enabled)
+ MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip);
+ wmb(); /* make sure registers committed */
}
static void mdss_dsi_8996_phy_power_off(
@@ -948,31 +999,51 @@ static void mdss_dsi_8996_phy_power_off(
int ln;
void __iomem *base;
u32 data;
+ struct mdss_panel_info *panel_info;
+
+ if (ctrl) {
+ panel_info = &((ctrl->panel_data).panel_info);
+ } else {
+ pr_warn("%s: null ctrl pdata\n", __func__);
+ return;
+ }
/* Turn off PLL power */
data = MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0);
MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, data & ~BIT(7));
- /* 4 lanes + clk lane configuration */
- for (ln = 0; ln < 5; ln++) {
- base = ctrl->phy_io.base +
- DATALANE_OFFSET_FROM_BASE_8996;
- base += (ln * DATALANE_SIZE_8996); /* lane base */
-
+ /* data lanes configuration */
+ base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996;
+ for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) {
/* turn off phy ldo */
- MIPI_OUTP(base + DSIPHY_LANE_VREG_CNTRL, 0x1c);
+ MIPI_OUTP(base + DSIPHY_LANE_VREG_BASE, 0x1c);
+ base += DATALANE_SIZE_8996; /* next lane */
}
- MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c);
- /* 4 lanes + clk lane configuration */
- for (ln = 0; ln < 5; ln++) {
- base = ctrl->phy_io.base +
- DATALANE_OFFSET_FROM_BASE_8996;
- base += (ln * DATALANE_SIZE_8996); /* lane base */
+ /* clk lane configuration */
+ base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996;
+ /* turn off phy ldo */
+ MIPI_OUTP(base + DSIPHY_LANE_VREG_BASE, 0x1c);
+ if (panel_info->split_link_enabled)
+ MIPI_OUTP(base + CLKLANE_SIZE_8996 +
+ DSIPHY_LANE_VREG_BASE, 0x1c);
+
+ MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c);
+ /* data lanes configuration */
+ base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996;
+ for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) {
MIPI_OUTP(base + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0);
+ base += DATALANE_SIZE_8996; /* next lane */
}
+ /* clk lane configuration */
+ base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996;
+ MIPI_OUTP(base + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0);
+ if (panel_info->split_link_enabled)
+ MIPI_OUTP(base + CLKLANE_SIZE_8996 +
+ DSIPHY_LANE_STRENGTH_CTRL_1, 0x0);
+
wmb(); /* make sure registers committed */
}
@@ -1008,22 +1079,46 @@ static void mdss_dsi_8996_phy_power_on(
struct mdss_dsi_phy_ctrl *pd;
char *ip;
u32 data;
+ struct mdss_panel_info *panel_info;
- pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db);
+ if (ctrl) {
+ panel_info = &((ctrl->panel_data).panel_info);
+ } else {
+ pr_warn("%s: null ctrl pdata\n", __func__);
+ return;
+ }
- /* 4 lanes + clk lane configuration */
- for (ln = 0; ln < 5; ln++) {
- base = ctrl->phy_io.base +
- DATALANE_OFFSET_FROM_BASE_8996;
- base += (ln * DATALANE_SIZE_8996); /* lane base */
+ pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db);
- /* strength, 2 * 5 */
- cnt = 2;
+ /* data lanes configuration */
+ base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996;
+ for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) {
+ /* strength, 2 * MDSS_DSI_NUM_DATA_LANES */
+ cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM;
ln_off = cnt * ln;
ip = &pd->strength[ln_off];
- off = 0x38;
- for (j = 0; j < cnt; j++, off += 4)
+ off = DSIPHY_LANE_STRENGTH_CTRL_BASE;
+ for (j = 0; j < cnt; j++,
+ off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET)
MIPI_OUTP(base + off, *ip++);
+ base += DATALANE_SIZE_8996; /* next lane */
+ }
+
+ /*
+ * clk lane configuration for strength ctrl
+ * for split link there are two clock lanes, one
+ * clock lane per sublink needs to be configured
+ */
+ base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996;
+ cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM;
+ ln_off = MDSS_DSI_NUM_DATA_LANES;
+ ip = &pd->strength[ln_off];
+ off = DSIPHY_LANE_STRENGTH_CTRL_BASE;
+ for (j = 0; j < cnt; j++,
+ off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET) {
+ MIPI_OUTP(base + off, *ip);
+ if (panel_info->split_link_enabled)
+ MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip);
}
mdss_dsi_8996_phy_regulator_enable(ctrl);
@@ -1051,67 +1146,129 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl)
int j, off, ln, cnt, ln_off;
char *ip;
void __iomem *base;
+ struct mdss_panel_info *panel_info;
+ int num_of_lanes = 0;
+
+ if (ctrl) {
+ panel_info = &((ctrl->panel_data).panel_info);
+ } else {
+ pr_warn("%s: null ctrl pdata\n", __func__);
+ return;
+ }
pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db);
+ num_of_lanes = MDSS_DSI_NUM_DATA_LANES + MDSS_DSI_NUM_CLK_LANES;
MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c);
/* clk_en */
MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL, 0x1);
- if (pd->lanecfg_len != 20) {
+ if (pd->lanecfg_len != (num_of_lanes * DSIPHY_LANE_CFG_NUM)) {
pr_err("%s: wrong lane cfg\n", __func__);
return;
}
- if (pd->strength_len != 10) {
+ if (pd->strength_len != (num_of_lanes *
+ DSIPHY_LANE_STRENGTH_CTRL_NUM)) {
pr_err("%s: wrong strength ctrl\n", __func__);
return;
}
- if (pd->regulator_len != 5) {
+ if (pd->regulator_len != (num_of_lanes * DSIPHY_LANE_VREG_NUM)) {
pr_err("%s: wrong regulator setting\n", __func__);
return;
}
- /* 4 lanes + clk lane configuration */
- for (ln = 0; ln < 5; ln++) {
- /*
- * data lane offset frome base: 0x100
- * data lane size: 0x80
- */
- base = ctrl->phy_io.base +
- DATALANE_OFFSET_FROM_BASE_8996;
- base += (ln * DATALANE_SIZE_8996); /* lane base */
-
- /* lane cfg, 4 * 5 */
- cnt = 4;
+ /* data lanes configuration */
+ base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996;
+ for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) {
+ /* lane cfg, 4 * MDSS_DSI_NUM_DATA_LANES */
+ cnt = DSIPHY_LANE_CFG_NUM;
+ off = DSIPHY_LANE_CFG_BASE;
ln_off = cnt * ln;
ip = &pd->lanecfg[ln_off];
- off = 0x0;
for (j = 0; j < cnt; j++) {
MIPI_OUTP(base + off, *ip++);
- off += 4;
+ off += DSIPHY_LANE_CFG_OFFSET;
}
/* test str */
- MIPI_OUTP(base + 0x14, 0x0088); /* fixed */
+ MIPI_OUTP(base + DSIPHY_LANE_TEST_STR, 0x88); /* fixed */
- /* phy timing, 8 * 5 */
- cnt = 8;
+ /* phy timing, 8 * MDSS_DSI_NUM_DATA_LANES */
+ cnt = DSIPHY_LANE_TIMING_CTRL_NUM;
+ off = DSIPHY_LANE_TIMING_CTRL_BASE;
ln_off = cnt * ln;
ip = &pd->timing_8996[ln_off];
- off = 0x18;
- for (j = 0; j < cnt; j++, off += 4)
+ for (j = 0; j < cnt; j++) {
MIPI_OUTP(base + off, *ip++);
+ off += DSIPHY_LANE_TIMING_CTRL_OFFSET;
+ }
- /* strength, 2 * 5 */
- cnt = 2;
+ /* strength, 2 * MDSS_DSI_NUM_DATA_LANES */
+ cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM;
+ off = DSIPHY_LANE_STRENGTH_CTRL_BASE;
ln_off = cnt * ln;
ip = &pd->strength[ln_off];
- off = 0x38;
- for (j = 0; j < cnt; j++, off += 4)
+ for (j = 0; j < cnt; j++) {
MIPI_OUTP(base + off, *ip++);
+ off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET;
+ }
+
+ base += DATALANE_SIZE_8996; /* next lane */
+ }
+
+ /*
+ * clk lane configuration
+ * for split link there are two clock lanes, one
+ * clock lane per sublink needs to be configured
+ */
+ base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996;
+ cnt = DSIPHY_LANE_CFG_NUM;
+ off = DSIPHY_LANE_CFG_BASE;
+ ln_off = cnt * MDSS_DSI_NUM_DATA_LANES;
+ ip = &pd->lanecfg[ln_off];
+ for (j = 0; j < cnt; j++) {
+ MIPI_OUTP(base + off, *ip);
+ if (panel_info->split_link_enabled)
+ MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip);
+ ip++;
+ off += DSIPHY_LANE_CFG_OFFSET;
+ }
+
+ /* test str */
+ MIPI_OUTP(base + DSIPHY_LANE_TEST_STR, 0x88); /* fixed */
+ if (panel_info->split_link_enabled)
+ MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, 0x88);
+
+ cnt = DSIPHY_LANE_TIMING_CTRL_NUM;
+ off = DSIPHY_LANE_TIMING_CTRL_BASE;
+ ln_off = cnt * MDSS_DSI_NUM_DATA_LANES;
+ ip = &pd->timing_8996[ln_off];
+ for (j = 0; j < cnt; j++) {
+ MIPI_OUTP(base + off, *ip);
+ if (panel_info->split_link_enabled)
+ MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip);
+ ip++;
+ off += DSIPHY_LANE_TIMING_CTRL_OFFSET;
+ }
+
+ /*
+ * clk lane configuration for timing
+ * for split link there are two clock lanes, one
+ * clock lane per sublink needs to be configured
+ */
+ cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM;
+ off = DSIPHY_LANE_STRENGTH_CTRL_BASE;
+ ln_off = cnt * MDSS_DSI_NUM_DATA_LANES;
+ ip = &pd->strength[ln_off];
+ for (j = 0; j < cnt; j++) {
+ MIPI_OUTP(base + off, *ip);
+ if (panel_info->split_link_enabled)
+ MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip);
+ ip++;
+ off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET;
}
wmb(); /* make sure registers committed */
@@ -1665,6 +1822,9 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info,
u32 dsi_pclk_rate;
u8 lanes = 0, bpp;
+ if (!panel_info)
+ return -EINVAL;
+
if (panel_info->mipi.data_lane3)
lanes += 1;
if (panel_info->mipi.data_lane2)
@@ -1690,6 +1850,8 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info,
}
h_period = mdss_panel_get_htotal(panel_info, true);
+ if (panel_info->split_link_enabled)
+ h_period *= panel_info->mipi.num_of_sublinks;
v_period = mdss_panel_get_vtotal(panel_info);
if (ctrl_pdata->refresh_clk_rate || is_diff_frame_rate(panel_info,
@@ -1710,7 +1872,12 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info,
clk_rate = panel_info->clk_rate;
do_div(clk_rate, 8 * bpp);
- dsi_pclk_rate = (u32) clk_rate * lanes;
+
+ if (panel_info->split_link_enabled)
+ dsi_pclk_rate = (u32) clk_rate *
+ panel_info->mipi.lanes_per_sublink;
+ else
+ dsi_pclk_rate = (u32) clk_rate * lanes;
if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 250000000))
dsi_pclk_rate = 35000000;
@@ -2320,6 +2487,32 @@ int mdss_dsi_pre_clkoff_cb(void *priv,
return rc;
}
+static void mdss_dsi_split_link_clk_cfg(struct mdss_dsi_ctrl_pdata *ctrl,
+ int enable)
+{
+ struct mdss_panel_data *pdata = NULL;
+ void __iomem *base;
+ u32 data = 0;
+
+ if (ctrl)
+ pdata = &ctrl->panel_data;
+ else {
+ pr_err("%s: ctrl pdata is NULL\n", __func__);
+ return;
+ }
+
+ /*
+ * for split link there are two clock lanes, and
+ * both clock lanes needs to be enabled
+ */
+ if (pdata->panel_info.split_link_enabled) {
+ base = ctrl->phy_io.base;
+ data = MIPI_INP(base + MDSS_DSI_DSIPHY_CMN_CLK_CFG1);
+ data |= (enable << DSIPHY_CMN_CLK_CFG1_SPLIT_LINK);
+ MIPI_OUTP(base + MDSS_DSI_DSIPHY_CMN_CLK_CFG1, data);
+ }
+}
+
int mdss_dsi_post_clkon_cb(void *priv,
enum mdss_dsi_clk_type clk,
enum mdss_dsi_clk_state curr_state)
@@ -2393,6 +2586,9 @@ int mdss_dsi_post_clkon_cb(void *priv,
}
if (pdata->panel_info.mipi.force_clk_lane_hs)
mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 1);
+
+ /* enable split link for cmn clk cfg1 */
+ mdss_dsi_split_link_clk_cfg(ctrl, 1);
}
error:
return rc;
@@ -2419,9 +2615,16 @@ int mdss_dsi_post_clkoff_cb(void *priv,
pdata = &ctrl->panel_data;
for (i = DSI_MAX_PM - 1; i >= DSI_CORE_PM; i--) {
- if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) &&
- (i != DSI_CORE_PM))
- continue;
+ /**
+ * If DSI_CTRL is active, proceed to turn off
+ * supplies which support turning off in low power
+ * state
+ */
+ if (ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE)
+ if (!sdata->power_data[i].vreg_config
+ ->lp_disable_allowed)
+ continue;
+
rc = msm_dss_enable_vreg(
sdata->power_data[i].vreg_config,
sdata->power_data[i].num_vreg, 0);
@@ -2431,6 +2634,12 @@ int mdss_dsi_post_clkoff_cb(void *priv,
__mdss_dsi_pm_name(i));
rc = 0;
} else {
+ pr_debug("%s: disabled vreg for %s panel_state %d\n",
+ __func__,
+ __mdss_dsi_pm_name(i),
+ pdata->panel_info.panel_power_state);
+ sdata->power_data[i].vreg_config->disabled =
+ true;
ctrl->core_power = false;
}
}
@@ -2470,7 +2679,7 @@ int mdss_dsi_pre_clkon_cb(void *priv,
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))
+ (!sdata->power_data[i].vreg_config->disabled))
continue;
rc = msm_dss_enable_vreg(
sdata->power_data[i].vreg_config,
@@ -2480,6 +2689,11 @@ int mdss_dsi_pre_clkon_cb(void *priv,
__func__,
__mdss_dsi_pm_name(i));
} else {
+ pr_debug("%s: enabled vregs for %s\n",
+ __func__,
+ __mdss_dsi_pm_name(i));
+ sdata->power_data[i].vreg_config->disabled =
+ false;
ctrl->core_power = true;
}
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 162689227a23..1cf907ecded4 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -533,6 +533,10 @@ hdmi_picture_aspect_get_name(enum hdmi_picture_aspect picture_aspect)
return "4:3";
case HDMI_PICTURE_ASPECT_16_9:
return "16:9";
+ case HDMI_PICTURE_ASPECT_64_27:
+ return "64:27";
+ case HDMI_PICTURE_ASPECT_256_135:
+ return "256:135";
case HDMI_PICTURE_ASPECT_RESERVED:
return "Reserved";
}
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 8959b320d472..84c6add93f1f 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -421,6 +421,8 @@ static int init_vqs(struct virtio_balloon *vb)
* Prime this virtqueue with one buffer so the hypervisor can
* use it to signal us later (it can't be broken yet!).
*/
+ update_balloon_stats(vb);
+
sg_init_one(&sg, vb->stats, sizeof vb->stats);
if (virtqueue_add_outbuf(vb->stats_vq, &sg, 1, vb, GFP_KERNEL)
< 0)
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
index 611f9c11da85..2e319d0c395d 100644
--- a/drivers/xen/xen-acpi-processor.c
+++ b/drivers/xen/xen-acpi-processor.c
@@ -27,10 +27,10 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/syscore_ops.h>
#include <linux/acpi.h>
#include <acpi/processor.h>
#include <xen/xen.h>
-#include <xen/xen-ops.h>
#include <xen/interface/platform.h>
#include <asm/xen/hypercall.h>
@@ -466,15 +466,33 @@ static int xen_upload_processor_pm_data(void)
return rc;
}
-static int xen_acpi_processor_resume(struct notifier_block *nb,
- unsigned long action, void *data)
+static void xen_acpi_processor_resume_worker(struct work_struct *dummy)
{
+ int rc;
+
bitmap_zero(acpi_ids_done, nr_acpi_bits);
- return xen_upload_processor_pm_data();
+
+ rc = xen_upload_processor_pm_data();
+ if (rc != 0)
+ pr_info("ACPI data upload failed, error = %d\n", rc);
+}
+
+static void xen_acpi_processor_resume(void)
+{
+ static DECLARE_WORK(wq, xen_acpi_processor_resume_worker);
+
+ /*
+ * xen_upload_processor_pm_data() calls non-atomic code.
+ * However, the context for xen_acpi_processor_resume is syscore
+ * with only the boot CPU online and in an atomic context.
+ *
+ * So defer the upload for some point safer.
+ */
+ schedule_work(&wq);
}
-struct notifier_block xen_acpi_processor_resume_nb = {
- .notifier_call = xen_acpi_processor_resume,
+static struct syscore_ops xap_syscore_ops = {
+ .resume = xen_acpi_processor_resume,
};
static int __init xen_acpi_processor_init(void)
@@ -527,7 +545,7 @@ static int __init xen_acpi_processor_init(void)
if (rc)
goto err_unregister;
- xen_resume_notifier_register(&xen_acpi_processor_resume_nb);
+ register_syscore_ops(&xap_syscore_ops);
return 0;
err_unregister:
@@ -544,7 +562,7 @@ static void __exit xen_acpi_processor_exit(void)
{
int i;
- xen_resume_notifier_unregister(&xen_acpi_processor_resume_nb);
+ unregister_syscore_ops(&xap_syscore_ops);
kfree(acpi_ids_done);
kfree(acpi_id_present);
kfree(acpi_id_cst_present);
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
index 929b618da43b..c30c6ceac2c4 100644
--- a/fs/9p/acl.c
+++ b/fs/9p/acl.c
@@ -283,6 +283,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
case ACL_TYPE_ACCESS:
if (acl) {
struct iattr iattr;
+ struct posix_acl *old_acl = acl;
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
if (retval)
@@ -293,6 +294,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
* by the mode bits. So don't
* update ACL.
*/
+ posix_acl_release(old_acl);
value = NULL;
size = 0;
}
diff --git a/fs/attr.c b/fs/attr.c
index 11be2265a2d5..c86b37c38fb7 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -211,7 +211,7 @@ int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr *
return -EPERM;
if (!inode_owner_or_capable(inode)) {
- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission2(mnt, inode, MAY_WRITE);
if (error)
return error;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index 7b8feb6d60c8..3a3b0f370304 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1364,7 +1364,7 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry)
goto out;
if (dentry->d_flags & DCACHE_SHRINK_LIST) {
- data->found++;
+ goto out;
} else {
if (dentry->d_flags & DCACHE_LRU_LIST)
d_lru_del(dentry);
diff --git a/fs/exec.c b/fs/exec.c
index 8c58183eccb7..b1f5ddbf7d56 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1132,8 +1132,10 @@ EXPORT_SYMBOL(flush_old_exec);
void would_dump(struct linux_binprm *bprm, struct file *file)
{
struct inode *inode = file_inode(file);
+
if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) {
struct user_namespace *old, *user_ns;
+
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
/* Ensure mm->user_ns contains the executable */
diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index 95a49ef2781a..ebaff5ab93da 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -106,6 +106,7 @@ config EXT4_ENCRYPTION
select CRYPTO_ECB
select CRYPTO_XTS
select CRYPTO_CTS
+ select CRYPTO_HEH
select CRYPTO_CTR
select CRYPTO_SHA256
select KEYS
diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c
index 2fbef8a14760..e2645ca9b95e 100644
--- a/fs/ext4/crypto_fname.c
+++ b/fs/ext4/crypto_fname.c
@@ -44,7 +44,8 @@ static void ext4_dir_crypt_complete(struct crypto_async_request *req, int res)
bool ext4_valid_filenames_enc_mode(uint32_t mode)
{
- return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS);
+ return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS ||
+ mode == EXT4_ENCRYPTION_MODE_AES_256_HEH);
}
static unsigned max_name_len(struct inode *inode)
diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
index 15342bfff70d..363e4c6bf37f 100644
--- a/fs/ext4/crypto_key.c
+++ b/fs/ext4/crypto_key.c
@@ -30,16 +30,16 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc)
}
/**
- * ext4_derive_key_aes() - Derive a key using AES-128-ECB
+ * ext4_derive_key_v1() - Derive a key using AES-128-ECB
* @deriving_key: Encryption key used for derivation.
* @source_key: Source key to which to apply derivation.
* @derived_key: Derived key.
*
- * Return: Zero on success; non-zero otherwise.
+ * Return: 0 on success, -errno on failure
*/
-static int ext4_derive_key_aes(char deriving_key[EXT4_AES_128_ECB_KEY_SIZE],
- char source_key[EXT4_AES_256_XTS_KEY_SIZE],
- char derived_key[EXT4_AES_256_XTS_KEY_SIZE])
+static int ext4_derive_key_v1(const char deriving_key[EXT4_AES_128_ECB_KEY_SIZE],
+ const char source_key[EXT4_AES_256_XTS_KEY_SIZE],
+ char derived_key[EXT4_AES_256_XTS_KEY_SIZE])
{
int res = 0;
struct ablkcipher_request *req = NULL;
@@ -84,6 +84,91 @@ out:
return res;
}
+/**
+ * ext4_derive_key_v2() - Derive a key non-reversibly
+ * @nonce: the nonce associated with the file
+ * @master_key: the master key referenced by the file
+ * @derived_key: (output) the resulting derived key
+ *
+ * This function computes the following:
+ * derived_key[0:127] = AES-256-ENCRYPT(master_key[0:255], nonce)
+ * derived_key[128:255] = AES-256-ENCRYPT(master_key[0:255], nonce ^ 0x01)
+ * derived_key[256:383] = AES-256-ENCRYPT(master_key[256:511], nonce)
+ * derived_key[384:511] = AES-256-ENCRYPT(master_key[256:511], nonce ^ 0x01)
+ *
+ * 'nonce ^ 0x01' denotes flipping the low order bit of the last byte.
+ *
+ * Unlike the v1 algorithm, the v2 algorithm is "non-reversible", meaning that
+ * compromising a derived key does not also compromise the master key.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int ext4_derive_key_v2(const char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE],
+ const char master_key[EXT4_MAX_KEY_SIZE],
+ char derived_key[EXT4_MAX_KEY_SIZE])
+{
+ const int noncelen = EXT4_KEY_DERIVATION_NONCE_SIZE;
+ struct crypto_cipher *tfm;
+ int err;
+ int i;
+
+ /*
+ * Since we only use each transform for a small number of encryptions,
+ * requesting just "aes" turns out to be significantly faster than
+ * "ecb(aes)", by about a factor of two.
+ */
+ tfm = crypto_alloc_cipher("aes", 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ BUILD_BUG_ON(4 * EXT4_KEY_DERIVATION_NONCE_SIZE != EXT4_MAX_KEY_SIZE);
+ BUILD_BUG_ON(2 * EXT4_AES_256_ECB_KEY_SIZE != EXT4_MAX_KEY_SIZE);
+ for (i = 0; i < 2; i++) {
+ memcpy(derived_key, nonce, noncelen);
+ memcpy(derived_key + noncelen, nonce, noncelen);
+ derived_key[2 * noncelen - 1] ^= 0x01;
+ err = crypto_cipher_setkey(tfm, master_key,
+ EXT4_AES_256_ECB_KEY_SIZE);
+ if (err)
+ break;
+ crypto_cipher_encrypt_one(tfm, derived_key, derived_key);
+ crypto_cipher_encrypt_one(tfm, derived_key + noncelen,
+ derived_key + noncelen);
+ master_key += EXT4_AES_256_ECB_KEY_SIZE;
+ derived_key += 2 * noncelen;
+ }
+ crypto_free_cipher(tfm);
+ return err;
+}
+
+/**
+ * ext4_derive_key() - Derive a per-file key from a nonce and master key
+ * @ctx: the encryption context associated with the file
+ * @master_key: the master key referenced by the file
+ * @derived_key: (output) the resulting derived key
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int ext4_derive_key(const struct ext4_encryption_context *ctx,
+ const char master_key[EXT4_MAX_KEY_SIZE],
+ char derived_key[EXT4_MAX_KEY_SIZE])
+{
+ BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE != EXT4_KEY_DERIVATION_NONCE_SIZE);
+ BUILD_BUG_ON(EXT4_AES_256_XTS_KEY_SIZE != EXT4_MAX_KEY_SIZE);
+
+ /*
+ * Although the key derivation algorithm is logically independent of the
+ * choice of encryption modes, in this kernel it is bundled with HEH
+ * encryption of filenames, which is another crypto improvement that
+ * requires an on-disk format change and requires userspace to specify
+ * different encryption policies.
+ */
+ if (ctx->filenames_encryption_mode == EXT4_ENCRYPTION_MODE_AES_256_HEH)
+ return ext4_derive_key_v2(ctx->nonce, master_key, derived_key);
+ else
+ return ext4_derive_key_v1(ctx->nonce, master_key, derived_key);
+}
+
void ext4_free_crypt_info(struct ext4_crypt_info *ci)
{
if (!ci)
@@ -192,6 +277,8 @@ retry:
break;
case EXT4_ENCRYPTION_MODE_PRIVATE:
cipher_str = "bugon";
+ case EXT4_ENCRYPTION_MODE_AES_256_HEH:
+ cipher_str = "heh(aes)";
break;
default:
printk_once(KERN_WARNING
@@ -242,7 +329,7 @@ retry:
up_read(&keyring_key->sem);
goto out;
}
- res = ext4_derive_key_aes(ctx.nonce, master_key->raw,
+ res = ext4_derive_key(&ctx, master_key->raw,
crypt_info->ci_raw_key);
up_read(&keyring_key->sem);
if (res)
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
index 8a9feb341f31..dd561f916f0b 100644
--- a/fs/ext4/crypto_policy.c
+++ b/fs/ext4/crypto_policy.c
@@ -156,6 +156,12 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent,
WARN_ON(1); /* Should never happen */
return 0;
}
+
+ /* No restrictions on file types which are never encrypted */
+ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
+ !S_ISLNK(child->i_mode))
+ return 1;
+
/* no restrictions if the parent directory is not encrypted */
if (!ext4_encrypted_inode(parent))
return 1;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 14d7f72f8092..482d1caee849 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -590,6 +590,7 @@ enum {
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
#define EXT4_ENCRYPTION_MODE_PRIVATE 127
+#define EXT4_ENCRYPTION_MODE_AES_256_HEH 126
#include "ext4_crypto.h"
@@ -2339,19 +2340,6 @@ 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;
@@ -2365,6 +2353,19 @@ 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;
+}
+
+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;
+}
+
#else
static inline int ext4_has_encryption_key(struct inode *inode)
{
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
index 95cbc9bc1995..e28cc5aab04a 100644
--- a/fs/ext4/ext4_crypto.h
+++ b/fs/ext4/ext4_crypto.h
@@ -59,8 +59,10 @@ struct ext4_encryption_context {
#define EXT4_XTS_TWEAK_SIZE 16
#define EXT4_AES_128_ECB_KEY_SIZE 16
#define EXT4_AES_256_GCM_KEY_SIZE 32
+#define EXT4_AES_256_ECB_KEY_SIZE 32
#define EXT4_AES_256_CBC_KEY_SIZE 32
#define EXT4_AES_256_CTS_KEY_SIZE 32
+#define EXT4_AES_256_HEH_KEY_SIZE 32
#define EXT4_AES_256_XTS_KEY_SIZE 64
#define EXT4_PRIVATE_KEY_SIZE 64
#define EXT4_MAX_KEY_SIZE 64
@@ -127,6 +129,8 @@ static inline int ext4_encryption_key_size(int mode)
return EXT4_AES_256_CBC_KEY_SIZE;
case EXT4_ENCRYPTION_MODE_AES_256_CTS:
return EXT4_AES_256_CTS_KEY_SIZE;
+ case EXT4_ENCRYPTION_MODE_AES_256_HEH:
+ return EXT4_AES_256_HEH_KEY_SIZE;
default:
BUG();
}
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index cb3d6f6419cd..280d67fe33a7 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -18,6 +18,7 @@
#include "ext4.h"
#include "xattr.h"
#include "truncate.h"
+#include <trace/events/android_fs.h>
#define EXT4_XATTR_SYSTEM_DATA "data"
#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS))
@@ -502,6 +503,17 @@ int ext4_readpage_inline(struct inode *inode, struct page *page)
return -EAGAIN;
}
+ if (trace_android_fs_dataread_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_dataread_start(inode, page_offset(page),
+ PAGE_SIZE, current->pid,
+ path, current->comm);
+ }
+
/*
* Current inline data can only exist in the 1st page,
* So for all the other pages, just set them uptodate.
@@ -513,6 +525,8 @@ int ext4_readpage_inline(struct inode *inode, struct page *page)
SetPageUptodate(page);
}
+ trace_android_fs_dataread_end(inode, page_offset(page), PAGE_SIZE);
+
up_read(&EXT4_I(inode)->xattr_sem);
unlock_page(page);
@@ -1157,10 +1171,9 @@ static int ext4_finish_convert_inline_dir(handle_t *handle,
set_buffer_uptodate(dir_block);
err = ext4_handle_dirty_dirent_node(handle, inode, dir_block);
if (err)
- goto out;
+ return err;
set_buffer_verified(dir_block);
-out:
- return err;
+ return ext4_mark_inode_dirty(handle, inode);
}
static int ext4_convert_inline_data_nolock(handle_t *handle,
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 99fa2fca52b1..72b384931f66 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -45,6 +45,7 @@
#include "ext4_ice.h"
#include <trace/events/ext4.h>
+#include <trace/events/android_fs.h>
#define MPAGE_DA_EXTENT_TAIL 0x01
@@ -1018,6 +1019,16 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
pgoff_t index;
unsigned from, to;
+ if (trace_android_fs_datawrite_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, pos, len,
+ current->pid, path,
+ current->comm);
+ }
trace_ext4_write_begin(inode, pos, len, flags);
/*
* Reserve one block more for addition to orphan list in case
@@ -1154,6 +1165,7 @@ static int ext4_write_end(struct file *file,
int ret = 0, ret2;
int i_size_changed = 0;
+ trace_android_fs_datawrite_end(inode, pos, len);
trace_ext4_write_end(inode, pos, len, copied);
if (ext4_test_inode_state(inode, EXT4_STATE_ORDERED_MODE)) {
ret = ext4_jbd2_file_inode(handle, inode);
@@ -1267,6 +1279,7 @@ static int ext4_journalled_write_end(struct file *file,
unsigned from, to;
int size_changed = 0;
+ trace_android_fs_datawrite_end(inode, pos, len);
trace_ext4_journalled_write_end(inode, pos, len, copied);
from = pos & (PAGE_CACHE_SIZE - 1);
to = from + len;
@@ -2742,6 +2755,16 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
len, flags, pagep, fsdata);
}
*fsdata = (void *)0;
+ if (trace_android_fs_datawrite_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, pos, len,
+ current->pid,
+ path, current->comm);
+ }
trace_ext4_da_write_begin(inode, pos, len, flags);
if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
@@ -2860,6 +2883,7 @@ static int ext4_da_write_end(struct file *file,
return ext4_write_end(file, mapping, pos,
len, copied, page, fsdata);
+ trace_android_fs_datawrite_end(inode, pos, len);
trace_ext4_da_write_end(inode, pos, len, copied);
start = pos & (PAGE_CACHE_SIZE - 1);
end = start + copied - 1;
@@ -3352,12 +3376,42 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
if (ext4_has_inline_data(inode))
return 0;
+ if (trace_android_fs_dataread_start_enabled() &&
+ (iov_iter_rw(iter) == READ)) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_dataread_start(inode, offset, count,
+ current->pid, path,
+ current->comm);
+ }
+ if (trace_android_fs_datawrite_start_enabled() &&
+ (iov_iter_rw(iter) == WRITE)) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, offset, count,
+ current->pid, path,
+ current->comm);
+ }
trace_ext4_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
ret = ext4_ext_direct_IO(iocb, iter, offset);
else
ret = ext4_ind_direct_IO(iocb, iter, offset);
trace_ext4_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), ret);
+
+ if (trace_android_fs_dataread_start_enabled() &&
+ (iov_iter_rw(iter) == READ))
+ trace_android_fs_dataread_end(inode, offset, count);
+ if (trace_android_fs_datawrite_start_enabled() &&
+ (iov_iter_rw(iter) == WRITE))
+ trace_android_fs_datawrite_end(inode, offset, count);
+
return ret;
}
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 7e974878d9a9..3a2594665b44 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -637,8 +637,12 @@ resizefs_out:
if (err)
goto encryption_policy_out;
+ mutex_lock(&inode->i_mutex);
+
err = ext4_process_policy(&policy, inode);
+ mutex_unlock(&inode->i_mutex);
+
mnt_drop_write_file(filp);
encryption_policy_out:
return err;
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index 71a08a294529..99f1bd8c7f05 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -46,6 +46,7 @@
#include "ext4.h"
#include "ext4_ice.h"
+#include <trace/events/android_fs.h>
/*
* Call ext4_decrypt on every single page, reusing the encryption
@@ -92,6 +93,17 @@ static inline bool ext4_bio_encrypted(struct bio *bio)
#endif
}
+static void
+ext4_trace_read_completion(struct bio *bio)
+{
+ struct page *first_page = bio->bi_io_vec[0].bv_page;
+
+ if (first_page != NULL)
+ trace_android_fs_dataread_end(first_page->mapping->host,
+ page_offset(first_page),
+ bio->bi_iter.bi_size);
+}
+
/*
* I/O completion handler for multipage BIOs.
*
@@ -109,6 +121,9 @@ static void mpage_end_io(struct bio *bio)
struct bio_vec *bv;
int i;
+ if (trace_android_fs_dataread_start_enabled())
+ ext4_trace_read_completion(bio);
+
if (ext4_bio_encrypted(bio)) {
struct ext4_crypto_ctx *ctx = bio->bi_private;
@@ -136,6 +151,30 @@ static void mpage_end_io(struct bio *bio)
bio_put(bio);
}
+static void
+ext4_submit_bio_read(struct bio *bio)
+{
+ if (trace_android_fs_dataread_start_enabled()) {
+ struct page *first_page = bio->bi_io_vec[0].bv_page;
+
+ if (first_page != NULL) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ first_page->mapping->host);
+ trace_android_fs_dataread_start(
+ first_page->mapping->host,
+ page_offset(first_page),
+ bio->bi_iter.bi_size,
+ current->pid,
+ path,
+ current->comm);
+ }
+ }
+ submit_bio(READ, bio);
+}
+
int ext4_mpage_readpages(struct address_space *mapping,
struct list_head *pages, struct page *page,
unsigned nr_pages)
@@ -277,7 +316,7 @@ int ext4_mpage_readpages(struct address_space *mapping,
*/
if (bio && (last_block_in_bio != blocks[0] - 1)) {
submit_and_realloc:
- submit_bio(READ, bio);
+ ext4_submit_bio_read(bio);
bio = NULL;
}
if (bio == NULL) {
@@ -309,14 +348,14 @@ int ext4_mpage_readpages(struct address_space *mapping,
if (((map.m_flags & EXT4_MAP_BOUNDARY) &&
(relative_block == map.m_len)) ||
(first_hole != blocks_per_page)) {
- submit_bio(READ, bio);
+ ext4_submit_bio_read(bio);
bio = NULL;
} else
last_block_in_bio = blocks[blocks_per_page - 1];
goto next_page;
confused:
if (bio) {
- submit_bio(READ, bio);
+ ext4_submit_bio_read(bio);
bio = NULL;
}
if (!PageUptodate(page))
@@ -329,6 +368,6 @@ int ext4_mpage_readpages(struct address_space *mapping,
}
BUG_ON(pages && !list_empty(pages));
if (bio)
- submit_bio(READ, bio);
+ ext4_submit_bio_read(bio);
return 0;
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 6fe8e30eeb99..68345a9e59b8 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3666,7 +3666,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
EXT4_DESC_PER_BLOCK(sb);
if (ext4_has_feature_meta_bg(sb)) {
- if (le32_to_cpu(es->s_first_meta_bg) >= db_count) {
+ if (le32_to_cpu(es->s_first_meta_bg) > db_count) {
ext4_msg(sb, KERN_WARNING,
"first meta block group too large: %u "
"(group descriptor block count %u)",
diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c
index e504f548b64e..5bbd1989d5e6 100644
--- a/fs/f2fs/crypto_policy.c
+++ b/fs/f2fs/crypto_policy.c
@@ -149,6 +149,11 @@ int f2fs_is_child_context_consistent_with_parent(struct inode *parent,
BUG_ON(1);
}
+ /* No restrictions on file types which are never encrypted */
+ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
+ !S_ISLNK(child->i_mode))
+ return 1;
+
/* no restrictions if the parent directory is not encrypted */
if (!f2fs_encrypted_inode(parent))
return 1;
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index f53826ec30f3..4fb5709256fd 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -26,6 +26,7 @@
#include "segment.h"
#include "trace.h"
#include <trace/events/f2fs.h>
+#include <trace/events/android_fs.h>
static void f2fs_read_end_io(struct bio *bio)
{
@@ -1405,6 +1406,16 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
struct dnode_of_data dn;
int err = 0;
+ if (trace_android_fs_datawrite_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, pos, len,
+ current->pid, path,
+ current->comm);
+ }
trace_f2fs_write_begin(inode, pos, len, flags);
f2fs_balance_fs(sbi);
@@ -1533,6 +1544,7 @@ static int f2fs_write_end(struct file *file,
{
struct inode *inode = page->mapping->host;
+ trace_android_fs_datawrite_end(inode, pos, len);
trace_f2fs_write_end(inode, pos, len, copied);
set_page_dirty(page);
@@ -1586,6 +1598,28 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
+ if (trace_android_fs_dataread_start_enabled() &&
+ (iov_iter_rw(iter) == READ)) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_dataread_start(inode, offset,
+ count, current->pid, path,
+ current->comm);
+ }
+ if (trace_android_fs_datawrite_start_enabled() &&
+ (iov_iter_rw(iter) == WRITE)) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, offset, count,
+ current->pid, path,
+ current->comm);
+ }
if (iov_iter_rw(iter) == WRITE) {
__allocate_data_blocks(inode, offset, count);
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) {
@@ -1599,6 +1633,13 @@ out:
if (err < 0 && iov_iter_rw(iter) == WRITE)
f2fs_write_failed(mapping, offset + count);
+ if (trace_android_fs_dataread_start_enabled() &&
+ (iov_iter_rw(iter) == READ))
+ trace_android_fs_dataread_end(inode, offset, count);
+ if (trace_android_fs_datawrite_start_enabled() &&
+ (iov_iter_rw(iter) == WRITE))
+ trace_android_fs_datawrite_end(inode, offset, count);
+
trace_f2fs_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), err);
return err;
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index a197215ad52b..4b449d263333 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1535,12 +1535,19 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg)
#ifdef CONFIG_F2FS_FS_ENCRYPTION
struct f2fs_encryption_policy policy;
struct inode *inode = file_inode(filp);
+ int err;
if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg,
sizeof(policy)))
return -EFAULT;
- return f2fs_process_policy(&policy, inode);
+ mutex_lock(&inode->i_mutex);
+
+ err = f2fs_process_policy(&policy, inode);
+
+ mutex_unlock(&inode->i_mutex);
+
+ return err;
#else
return -EOPNOTSUPP;
#endif
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index bda7126466c0..dbb2cc4df603 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -13,6 +13,7 @@
#include "f2fs.h"
#include "node.h"
+#include <trace/events/android_fs.h>
bool f2fs_may_inline_data(struct inode *inode)
{
@@ -84,14 +85,29 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
{
struct page *ipage;
+ if (trace_android_fs_dataread_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_dataread_start(inode, page_offset(page),
+ PAGE_SIZE, current->pid,
+ path, current->comm);
+ }
+
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
if (IS_ERR(ipage)) {
+ trace_android_fs_dataread_end(inode, page_offset(page),
+ PAGE_SIZE);
unlock_page(page);
return PTR_ERR(ipage);
}
if (!f2fs_has_inline_data(inode)) {
f2fs_put_page(ipage, 1);
+ trace_android_fs_dataread_end(inode, page_offset(page),
+ PAGE_SIZE);
return -EAGAIN;
}
@@ -102,6 +118,8 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
SetPageUptodate(page);
f2fs_put_page(ipage, 1);
+ trace_android_fs_dataread_end(inode, page_offset(page),
+ PAGE_SIZE);
unlock_page(page);
return 0;
}
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index ff0ac96a8e7b..db4c867369b5 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -78,8 +78,11 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (type == ACL_TYPE_ACCESS) {
umode_t mode = inode->i_mode;
-
+ struct posix_acl *old_acl = acl;
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+
+ if (!acl)
+ posix_acl_release(old_acl);
if (error)
return error;
if (mode != inode->i_mode)
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index de7b4f97ac75..be519416c112 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -207,7 +207,7 @@ struct lm_lockname {
struct gfs2_sbd *ln_sbd;
u64 ln_number;
unsigned int ln_type;
-};
+} __packed __aligned(sizeof(int));
#define lm_name_equal(name1, name2) \
(((name1)->ln_number == (name2)->ln_number) && \
diff --git a/fs/mpage.c b/fs/mpage.c
index 1480d3a18037..0fd48fdcc1b1 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -30,6 +30,14 @@
#include <linux/cleancache.h>
#include "internal.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/android_fs.h>
+
+EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_start);
+EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_end);
+EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_start);
+EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_end);
+
/*
* I/O completion handler for multipage BIOs.
*
@@ -47,6 +55,16 @@ static void mpage_end_io(struct bio *bio)
struct bio_vec *bv;
int i;
+ if (trace_android_fs_dataread_end_enabled() &&
+ (bio_data_dir(bio) == READ)) {
+ struct page *first_page = bio->bi_io_vec[0].bv_page;
+
+ if (first_page != NULL)
+ trace_android_fs_dataread_end(first_page->mapping->host,
+ page_offset(first_page),
+ bio->bi_iter.bi_size);
+ }
+
bio_for_each_segment_all(bv, bio, i) {
struct page *page = bv->bv_page;
page_endio(page, bio_data_dir(bio), bio->bi_error);
@@ -57,6 +75,24 @@ static void mpage_end_io(struct bio *bio)
static struct bio *mpage_bio_submit(int rw, struct bio *bio)
{
+ if (trace_android_fs_dataread_start_enabled() && (rw == READ)) {
+ struct page *first_page = bio->bi_io_vec[0].bv_page;
+
+ if (first_page != NULL) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ first_page->mapping->host);
+ trace_android_fs_dataread_start(
+ first_page->mapping->host,
+ page_offset(first_page),
+ bio->bi_iter.bi_size,
+ current->pid,
+ path,
+ current->comm);
+ }
+ }
bio->bi_end_io = mpage_end_io;
guard_bio_eod(rw, bio);
submit_bio(rw, bio);
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
index 971928ab6c21..8e31d1a80f0c 100644
--- a/fs/sdcardfs/dentry.c
+++ b/fs/sdcardfs/dentry.c
@@ -46,7 +46,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
spin_unlock(&dentry->d_lock);
/* check uninitialized obb_dentry and
- * whether the base obbpath has been changed or not */
+ * whether the base obbpath has been changed or not
+ */
if (is_obbpath_invalid(dentry)) {
d_drop(dentry);
return 0;
@@ -76,17 +77,13 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
if (dentry < lower_dentry) {
spin_lock(&dentry->d_lock);
- spin_lock(&lower_dentry->d_lock);
+ spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED);
} else {
spin_lock(&lower_dentry->d_lock);
- spin_lock(&dentry->d_lock);
+ spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
}
- 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) {
+ if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) {
__d_drop(dentry);
err = 0;
}
@@ -110,12 +107,10 @@ out:
static void sdcardfs_d_release(struct dentry *dentry)
{
/* release and reset the lower paths */
- if(has_graft_path(dentry)) {
+ 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,
@@ -132,12 +127,10 @@ static int sdcardfs_hash_ci(const struct dentry *dentry,
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);
@@ -151,35 +144,25 @@ 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;
+ /* FIXME Should we support national language? */
- /* 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)
+ if (str_n_case_eq(name->name, str, len))
return 0;
}
return 1;
}
-static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) {
+static void sdcardfs_canonical_path(const struct path *path,
+ struct path *actual_path)
+{
sdcardfs_get_real_lower(path->dentry, actual_path);
}
const struct dentry_operations sdcardfs_ci_dops = {
.d_revalidate = sdcardfs_d_revalidate,
.d_release = sdcardfs_d_release,
- .d_hash = sdcardfs_hash_ci,
+ .d_hash = sdcardfs_hash_ci,
.d_compare = sdcardfs_cmp_ci,
.d_canonical_path = sdcardfs_canonical_path,
};
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index 0bb442338a85..f82fedd29007 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -37,7 +37,8 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
/* 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 inode *top)
+ uid_t uid, bool under_android,
+ struct inode *top)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
@@ -50,12 +51,17 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
set_top(info, top);
}
-/* 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, const struct qstr *name)
+/* 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,
+ const struct qstr *name)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
+ struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent));
appid_t appid;
+ unsigned long user_num;
+ int err;
struct qstr q_Android = QSTR_LITERAL("Android");
struct qstr q_data = QSTR_LITERAL("data");
struct qstr q_obb = QSTR_LITERAL("obb");
@@ -77,58 +83,61 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co
return;
/* Derive custom permissions based on parent and current node */
switch (parent_info->perm) {
- case PERM_INHERIT:
- case PERM_ANDROID_PACKAGE_CACHE:
- /* Already inherited above */
- break;
- case PERM_PRE_ROOT:
- /* Legacy internal layout places users at top level */
- info->perm = PERM_ROOT;
- info->userid = simple_strtoul(name->name, NULL, 10);
+ case PERM_INHERIT:
+ case PERM_ANDROID_PACKAGE_CACHE:
+ /* Already inherited above */
+ break;
+ case PERM_PRE_ROOT:
+ /* Legacy internal layout places users at top level */
+ info->perm = PERM_ROOT;
+ err = kstrtoul(name->name, 10, &user_num);
+ if (err)
+ info->userid = 0;
+ else
+ info->userid = user_num;
+ set_top(info, &info->vfs_inode);
+ break;
+ case PERM_ROOT:
+ /* Assume masked off by default. */
+ if (qstr_case_eq(name, &q_Android)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->perm = PERM_ANDROID;
+ info->under_android = true;
set_top(info, &info->vfs_inode);
- break;
- case PERM_ROOT:
- /* Assume masked off by default. */
- if (qstr_case_eq(name, &q_Android)) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID;
- info->under_android = true;
- set_top(info, &info->vfs_inode);
- }
- break;
- case PERM_ANDROID:
- if (qstr_case_eq(name, &q_data)) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_DATA;
- set_top(info, &info->vfs_inode);
- } else if (qstr_case_eq(name, &q_obb)) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_OBB;
- info->under_obb = true;
- set_top(info, &info->vfs_inode);
- /* Single OBB directory is always shared */
- } else if (qstr_case_eq(name, &q_media)) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_MEDIA;
- set_top(info, &info->vfs_inode);
- }
- break;
- case PERM_ANDROID_OBB:
- case PERM_ANDROID_DATA:
- case PERM_ANDROID_MEDIA:
- info->perm = PERM_ANDROID_PACKAGE;
- appid = get_appid(name->name);
- if (appid != 0 && !is_excluded(name->name, parent_info->userid)) {
- info->d_uid = multiuser_get_uid(parent_info->userid, appid);
- }
+ }
+ break;
+ case PERM_ANDROID:
+ if (qstr_case_eq(name, &q_data)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->perm = PERM_ANDROID_DATA;
set_top(info, &info->vfs_inode);
- break;
- case PERM_ANDROID_PACKAGE:
- if (qstr_case_eq(name, &q_cache)) {
- info->perm = PERM_ANDROID_PACKAGE_CACHE;
- info->under_cache = true;
- }
- break;
+ } else if (qstr_case_eq(name, &q_obb)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->perm = PERM_ANDROID_OBB;
+ info->under_obb = true;
+ set_top(info, &info->vfs_inode);
+ /* Single OBB directory is always shared */
+ } else if (qstr_case_eq(name, &q_media)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->perm = PERM_ANDROID_MEDIA;
+ set_top(info, &info->vfs_inode);
+ }
+ break;
+ case PERM_ANDROID_OBB:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ info->perm = PERM_ANDROID_PACKAGE;
+ appid = get_appid(name->name);
+ if (appid != 0 && !is_excluded(name->name, parent_info->userid))
+ info->d_uid = multiuser_get_uid(parent_info->userid, appid);
+ set_top(info, &info->vfs_inode);
+ break;
+ case PERM_ANDROID_PACKAGE:
+ if (qstr_case_eq(name, &q_cache)) {
+ info->perm = PERM_ANDROID_PACKAGE_CACHE;
+ info->under_cache = true;
+ }
+ break;
}
}
@@ -137,7 +146,8 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry)
get_derived_permission_new(parent, dentry, &dentry->d_name);
}
-static appid_t get_type(const char *name) {
+static appid_t get_type(const char *name)
+{
const char *ext = strrchr(name, '.');
appid_t id;
@@ -149,7 +159,8 @@ static appid_t get_type(const char *name) {
return AID_MEDIA_RW;
}
-void fixup_lower_ownership(struct dentry* dentry, const char *name) {
+void fixup_lower_ownership(struct dentry *dentry, const char *name)
+{
struct path path;
struct inode *inode;
struct inode *delegated_inode = NULL;
@@ -175,49 +186,49 @@ void fixup_lower_ownership(struct dentry* dentry, const char *name) {
}
switch (perm) {
- case PERM_ROOT:
- case PERM_ANDROID:
- case PERM_ANDROID_DATA:
- case PERM_ANDROID_MEDIA:
- case PERM_ANDROID_PACKAGE:
- case PERM_ANDROID_PACKAGE_CACHE:
- uid = multiuser_get_uid(info->userid, uid);
- break;
- case PERM_ANDROID_OBB:
- uid = AID_MEDIA_OBB;
- break;
- case PERM_PRE_ROOT:
- default:
- break;
+ case PERM_ROOT:
+ case PERM_ANDROID:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ case PERM_ANDROID_PACKAGE:
+ case PERM_ANDROID_PACKAGE_CACHE:
+ uid = multiuser_get_uid(info->userid, uid);
+ break;
+ case PERM_ANDROID_OBB:
+ uid = AID_MEDIA_OBB;
+ break;
+ case PERM_PRE_ROOT:
+ default:
+ break;
}
switch (perm) {
- case PERM_ROOT:
- case PERM_ANDROID:
- case PERM_ANDROID_DATA:
- case PERM_ANDROID_MEDIA:
- if (S_ISDIR(d_inode(dentry)->i_mode))
- gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
- else
- gid = multiuser_get_uid(info->userid, get_type(name));
- break;
- case PERM_ANDROID_OBB:
- gid = AID_MEDIA_OBB;
- break;
- case PERM_ANDROID_PACKAGE:
- if (info->d_uid != 0)
- gid = multiuser_get_ext_gid(info->userid, info->d_uid);
- else
- gid = multiuser_get_uid(info->userid, uid);
- break;
- case PERM_ANDROID_PACKAGE_CACHE:
- if (info->d_uid != 0)
- gid = multiuser_get_cache_gid(info->userid, info->d_uid);
- else
- gid = multiuser_get_uid(info->userid, uid);
- break;
- case PERM_PRE_ROOT:
- default:
- break;
+ case PERM_ROOT:
+ case PERM_ANDROID:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ if (S_ISDIR(d_inode(dentry)->i_mode))
+ gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
+ else
+ gid = multiuser_get_uid(info->userid, get_type(name));
+ break;
+ case PERM_ANDROID_OBB:
+ gid = AID_MEDIA_OBB;
+ break;
+ case PERM_ANDROID_PACKAGE:
+ if (info->d_uid != 0)
+ gid = multiuser_get_ext_gid(info->d_uid);
+ else
+ gid = multiuser_get_uid(info->userid, uid);
+ break;
+ case PERM_ANDROID_PACKAGE_CACHE:
+ if (info->d_uid != 0)
+ gid = multiuser_get_cache_gid(info->d_uid);
+ else
+ gid = multiuser_get_uid(info->userid, uid);
+ break;
+ case PERM_PRE_ROOT:
+ default:
+ break;
}
sdcardfs_get_lower_path(dentry, &path);
@@ -243,9 +254,11 @@ retry_deleg:
if (error)
pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n");
}
+ sdcardfs_put_lower_path(dentry, &path);
}
-static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) {
+static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit)
+{
if (info->perm == PERM_ROOT)
return (limit->flags & BY_USERID)?info->userid == limit->userid:1;
if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID)
@@ -253,85 +266,56 @@ static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct li
return 0;
}
-static int needs_fixup(perm_t perm) {
+static int needs_fixup(perm_t perm)
+{
if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
|| perm == PERM_ANDROID_MEDIA)
return 1;
return 0;
}
-void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) {
+static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth)
+{
struct dentry *child;
struct sdcardfs_inode_info *info;
- if (!dget(dentry))
- return;
+
+ /*
+ * All paths will terminate their recursion on hitting PERM_ANDROID_OBB,
+ * PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of
+ * at most 3.
+ */
+ WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__);
+ spin_lock_nested(&dentry->d_lock, depth);
if (!d_inode(dentry)) {
- dput(dentry);
+ spin_unlock(&dentry->d_lock);
return;
}
info = SDCARDFS_I(d_inode(dentry));
if (needs_fixup(info->perm)) {
- spin_lock(&dentry->d_lock);
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
- dget(child);
- if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) {
+ spin_lock_nested(&child->d_lock, depth + 1);
+ if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) {
if (d_inode(child)) {
get_derived_permission(dentry, child);
fixup_tmp_permissions(d_inode(child));
- dput(child);
+ spin_unlock(&child->d_lock);
break;
}
}
- dput(child);
+ spin_unlock(&child->d_lock);
}
- spin_unlock(&dentry->d_lock);
- } else if (descendant_may_need_fixup(info, limit)) {
- spin_lock(&dentry->d_lock);
+ } else if (descendant_may_need_fixup(info, limit)) {
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
- fixup_perms_recursive(child, limit);
- }
- spin_unlock(&dentry->d_lock);
- }
- dput(dentry);
-}
-
-void drop_recursive(struct dentry *parent) {
- struct dentry *dentry;
- struct sdcardfs_inode_info *info;
- if (!d_inode(parent))
- return;
- info = SDCARDFS_I(d_inode(parent));
- spin_lock(&parent->d_lock);
- list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
- if (d_inode(dentry)) {
- if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
- drop_recursive(dentry);
- d_drop(dentry);
- }
+ __fixup_perms_recursive(child, limit, depth + 1);
}
}
- spin_unlock(&parent->d_lock);
+ spin_unlock(&dentry->d_lock);
}
-void fixup_top_recursive(struct dentry *parent) {
- struct dentry *dentry;
- struct sdcardfs_inode_info *info;
-
- if (!d_inode(parent))
- return;
- info = SDCARDFS_I(d_inode(parent));
- spin_lock(&parent->d_lock);
- list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
- if (d_inode(dentry)) {
- if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
- get_derived_permission(parent, dentry);
- fixup_tmp_permissions(d_inode(dentry));
- fixup_top_recursive(dentry);
- }
- }
- }
- spin_unlock(&parent->d_lock);
+void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit)
+{
+ __fixup_perms_recursive(dentry, limit, 0);
}
/* main function for updating derived permission */
@@ -339,19 +323,17 @@ inline void update_derived_permission_lock(struct dentry *dentry)
{
struct dentry *parent;
- if(!dentry || !d_inode(dentry)) {
- printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
+ if (!dentry || !d_inode(dentry)) {
+ pr_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
*/
- if(IS_ROOT(dentry)) {
- //setup_default_pre_root_state(d_inode(dentry));
- } else {
+ if (!IS_ROOT(dentry)) {
parent = dget_parent(dentry);
- if(parent) {
+ if (parent) {
get_derived_permission(parent, dentry);
dput(parent);
}
@@ -363,15 +345,15 @@ int need_graft_path(struct dentry *dentry)
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
+ struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent));
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
struct qstr obb = QSTR_LITERAL("obb");
- if(parent_info->perm == PERM_ANDROID &&
+ if (parent_info->perm == PERM_ANDROID &&
qstr_case_eq(&dentry->d_name, &obb)) {
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
- if(!(sbi->options.multiuser == false
+ if (!(sbi->options.multiuser == false
&& parent_info->userid == 0)) {
ret = 1;
}
@@ -386,22 +368,24 @@ int is_obbpath_invalid(struct dentry *dent)
struct sdcardfs_dentry_info *di = SDCARDFS_D(dent);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb);
char *path_buf, *obbpath_s;
+ int need_put = 0;
+ struct path lower_path;
/* 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() */
+ * regarding the uninitialized obb, refer to the sdcardfs_mkdir()
+ */
spin_lock(&di->lock);
- if(di->orig_path.dentry) {
- if(!di->lower_path.dentry) {
+ 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) {
+ if (!path_buf) {
ret = 1;
- printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__);
+ pr_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) ||
@@ -411,11 +395,13 @@ int is_obbpath_invalid(struct dentry *dent)
kfree(path_buf);
}
- //unlock_dir(lower_parent);
- path_put(&di->lower_path);
+ pathcpy(&lower_path, &di->lower_path);
+ need_put = 1;
}
}
spin_unlock(&di->lock);
+ if (need_put)
+ path_put(&lower_path);
return ret;
}
@@ -423,13 +409,13 @@ int is_base_obbpath(struct dentry *dentry)
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
+ struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent));
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
struct qstr q_obb = QSTR_LITERAL("obb");
spin_lock(&SDCARDFS_D(dentry)->lock);
if (sbi->options.multiuser) {
- if(parent_info->perm == PERM_PRE_ROOT &&
+ if (parent_info->perm == PERM_PRE_ROOT &&
qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
@@ -444,7 +430,8 @@ int is_base_obbpath(struct dentry *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) */
+ * returns: -ERRNO if error (0: no error)
+ */
int setup_obb_dentry(struct dentry *dentry, struct path *lower_path)
{
int err = 0;
@@ -453,23 +440,24 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path)
/* 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. */
+ * 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) {
+ 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");
+ * a lookup fail.
+ */
+ pr_info("sdcardfs: the sbi->obbpath is not available\n");
}
return err;
}
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index 23f8cd7f8877..c0146e03fa2e 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -65,7 +65,7 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf,
/* check disk space */
if (!check_min_free_space(dentry, count, 0)) {
- printk(KERN_INFO "No minimum free space.\n");
+ pr_err("No minimum free space.\n");
return -ENOSPC;
}
@@ -113,6 +113,10 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
if (lower_file->f_op->unlocked_ioctl)
err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
+ /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */
+ if (!err)
+ sdcardfs_copy_and_fix_attrs(file_inode(file),
+ file_inode(lower_file));
out:
return err;
}
@@ -160,8 +164,7 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma)
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");
+ pr_err("sdcardfs: lower file system does not support writeable mmap\n");
goto out;
}
@@ -173,16 +176,10 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma)
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);
+ pr_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;
- }
}
/*
@@ -216,10 +213,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
goto out_err;
}
- if(!check_caller_access_to_name(d_inode(parent), &dentry->d_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);
+ if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
err = -EACCES;
goto out_err;
}
@@ -251,9 +245,8 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
if (err)
kfree(SDCARDFS_F(file));
- else {
+ else
sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode));
- }
out_revert_cred:
REVERT_CRED(saved_cred);
@@ -323,6 +316,75 @@ static int sdcardfs_fasync(int fd, struct file *file, int flag)
return err;
}
+/*
+ * Sdcardfs cannot use generic_file_llseek as ->llseek, because it would
+ * only set the offset of the upper file. So we have to implement our
+ * own method to set both the upper and lower file offsets
+ * consistently.
+ */
+static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence)
+{
+ int err;
+ struct file *lower_file;
+
+ err = generic_file_llseek(file, offset, whence);
+ if (err < 0)
+ goto out;
+
+ lower_file = sdcardfs_lower_file(file);
+ err = generic_file_llseek(lower_file, offset, whence);
+
+out:
+ return err;
+}
+
+/*
+ * Sdcardfs read_iter, redirect modified iocb to lower read_iter
+ */
+ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter)
+{
+ int err;
+ struct file *file = iocb->ki_filp, *lower_file;
+
+ lower_file = sdcardfs_lower_file(file);
+ if (!lower_file->f_op->read_iter) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ get_file(lower_file); /* prevent lower_file from being released */
+ iocb->ki_filp = lower_file;
+ err = lower_file->f_op->read_iter(iocb, iter);
+ /* ? wait IO finish to update atime as ecryptfs ? */
+ iocb->ki_filp = file;
+ fput(lower_file);
+out:
+ return err;
+}
+
+/*
+ * Sdcardfs write_iter, redirect modified iocb to lower write_iter
+ */
+ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter)
+{
+ int err;
+ struct file *file = iocb->ki_filp, *lower_file;
+
+ lower_file = sdcardfs_lower_file(file);
+ if (!lower_file->f_op->write_iter) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ get_file(lower_file); /* prevent lower_file from being released */
+ iocb->ki_filp = lower_file;
+ err = lower_file->f_op->write_iter(iocb, iter);
+ iocb->ki_filp = file;
+ fput(lower_file);
+out:
+ return err;
+}
+
const struct file_operations sdcardfs_main_fops = {
.llseek = generic_file_llseek,
.read = sdcardfs_read,
@@ -337,11 +399,13 @@ const struct file_operations sdcardfs_main_fops = {
.release = sdcardfs_file_release,
.fsync = sdcardfs_fsync,
.fasync = sdcardfs_fasync,
+ .read_iter = sdcardfs_read_iter,
+ .write_iter = sdcardfs_write_iter,
};
/* trimmed directory options */
const struct file_operations sdcardfs_dir_fops = {
- .llseek = generic_file_llseek,
+ .llseek = sdcardfs_file_llseek,
.read = generic_read_dir,
.iterate = sdcardfs_readdir,
.unlocked_ioctl = sdcardfs_unlocked_ioctl,
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 68e615045616..f15cb11ca8fd 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -20,12 +20,13 @@
#include "sdcardfs.h"
#include <linux/fs_struct.h>
+#include <linux/ratelimit.h>
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info)
+const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info)
{
- struct cred * cred;
- const struct cred * old_cred;
+ struct cred *cred;
+ const struct cred *old_cred;
uid_t uid;
cred = prepare_creds();
@@ -45,9 +46,9 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs
}
/* Do not directly use this function, use REVERT_CRED() instead. */
-void revert_fsids(const struct cred * old_cred)
+void revert_fsids(const struct cred *old_cred)
{
- const struct cred * cur_cred;
+ const struct cred *cur_cred;
cur_cred = current->cred;
revert_creds(old_cred);
@@ -66,10 +67,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
struct fs_struct *saved_fs;
struct fs_struct *copied_fs;
- if(!check_caller_access_to_name(dir, &dentry->d_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);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
@@ -168,10 +166,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
struct path lower_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(dir, &dentry->d_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);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
@@ -245,14 +240,15 @@ out:
}
#endif
-static int touch(char *abs_path, mode_t mode) {
+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",
+ } else {
+ pr_err("sdcardfs: failed to open(%s): %ld\n",
abs_path, PTR_ERR(filp));
return PTR_ERR(filp);
}
@@ -278,10 +274,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
struct qstr q_obb = QSTR_LITERAL("obb");
struct qstr q_data = QSTR_LITERAL("data");
- if(!check_caller_access_to_name(dir, &dentry->d_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);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
@@ -291,7 +284,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
/* check disk space */
if (!check_min_free_space(dentry, 0, 1)) {
- printk(KERN_INFO "sdcardfs: No minimum free space.\n");
+ pr_err("sdcardfs: No minimum free space.\n");
err = -ENOSPC;
goto out_revert;
}
@@ -323,19 +316,21 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
}
/* if it is a local obb dentry, setup it with the base obbpath */
- if(need_graft_path(dentry)) {
+ if (need_graft_path(dentry)) {
err = setup_obb_dentry(dentry, &lower_path);
- if(err) {
+ 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 */
+ * 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 */
+ * therefore, an additional path_get is required
+ */
path_get(&lower_path);
} else
make_nomedia_in_obb = 1;
@@ -365,7 +360,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
set_fs_pwd(current->fs, &lower_path);
touch_err = touch(".nomedia", 0664);
if (touch_err) {
- printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n",
+ pr_err("sdcardfs: failed to create .nomedia in %s: %d\n",
lower_path.dentry->d_name.name, touch_err);
goto out;
}
@@ -390,10 +385,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
struct path lower_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(dir, &dentry->d_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);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
@@ -402,7 +394,8 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
- * the dentry on the original path should be deleted. */
+ * the dentry on the original path should be deleted.
+ */
sdcardfs_get_real_lower(dentry, &lower_path);
lower_dentry = lower_path.dentry;
@@ -478,11 +471,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
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) ||
+ if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) ||
!check_caller_access_to_name(new_dir, &new_dentry->d_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;
}
@@ -528,7 +518,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name);
fixup_tmp_permissions(d_inode(old_dentry));
fixup_lower_ownership(old_dentry, new_dentry->d_name.name);
- drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */
+ d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */
out:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_old_dir_dentry);
@@ -599,7 +589,7 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie)
static int sdcardfs_permission_wrn(struct inode *inode, int mask)
{
- WARN(1, "sdcardfs does not support permission. Use permission2.\n");
+ WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n");
return -EINVAL;
}
@@ -652,7 +642,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma
release_top(SDCARDFS_I(inode));
tmp.i_sb = inode->i_sb;
if (IS_POSIXACL(inode))
- printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__);
+ pr_warn("%s: This may be undefined behavior...\n", __func__);
err = generic_permission(&tmp, mask);
/* XXX
* Original sdcardfs code calls inode_permission(lower_inode,.. )
@@ -670,6 +660,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma
* we check it with AID_MEDIA_RW permission
*/
struct inode *lower_inode;
+
OVERRIDE_CRED(SDCARDFS_SB(inode->sb));
lower_inode = sdcardfs_lower_inode(inode);
@@ -684,7 +675,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma
static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
{
- WARN(1, "sdcardfs does not support setattr. User setattr2.\n");
+ WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n");
return -EINVAL;
}
@@ -738,19 +729,16 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
/* prepare our own lower struct iattr (with the lower file) */
memcpy(&lower_ia, ia, sizeof(lower_ia));
/* Allow touch updating timestamps. A previous permission check ensures
- * we have write access. Changes to mode, owner, and group are ignored*/
+ * we have write access. Changes to mode, owner, and group are ignored
+ */
ia->ia_valid |= ATTR_FORCE;
err = inode_change_ok(&tmp, ia);
if (!err) {
/* check the Android group ID */
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(d_inode(parent), &dentry->d_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);
+ if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name))
err = -EACCES;
- }
dput(parent);
}
@@ -828,10 +816,12 @@ out_err:
return err;
}
-static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat)
+static int sdcardfs_fillattr(struct vfsmount *mnt,
+ struct inode *inode, struct kstat *stat)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
struct inode *top = grab_top(info);
+
if (!top)
return -EINVAL;
@@ -855,33 +845,27 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct k
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 kstat lower_stat;
struct path lower_path;
struct dentry *parent;
int err;
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(d_inode(parent), &dentry->d_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);
+ if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
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);
-
- err = sdcardfs_fillattr(mnt, inode, stat);
+ err = vfs_getattr(&lower_path, &lower_stat);
+ if (err)
+ goto out;
+ sdcardfs_copy_and_fix_attrs(d_inode(dentry),
+ d_inode(lower_path.dentry));
+ err = sdcardfs_fillattr(mnt, d_inode(dentry), stat);
+ stat->blocks = lower_stat.blocks;
+out:
sdcardfs_put_lower_path(dentry, &lower_path);
return err;
}
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index 9135866b7766..f10f7752f514 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -36,8 +36,7 @@ int sdcardfs_init_dentry_cache(void)
void sdcardfs_destroy_dentry_cache(void)
{
- if (sdcardfs_dentry_cachep)
- kmem_cache_destroy(sdcardfs_dentry_cachep);
+ kmem_cache_destroy(sdcardfs_dentry_cachep);
}
void free_dentry_private_data(struct dentry *dentry)
@@ -73,6 +72,7 @@ static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *
{
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 */
@@ -102,7 +102,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u
* instead.
*/
lower_inode->i_ino, /* hashval */
- sdcardfs_inode_test, /* inode comparison function */
+ sdcardfs_inode_test, /* inode comparison function */
sdcardfs_inode_set, /* inode init function */
&data); /* data passed to test+set fxns */
if (!inode) {
@@ -164,27 +164,25 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u
}
/*
- * 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)
+ * Helper interpose routine, called directly by ->lookup to handle
+ * spliced dentries.
*/
-int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
- struct path *lower_path, userid_t id)
+static struct dentry *__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;
+ struct dentry *ret_dentry;
lower_inode = d_inode(lower_path->dentry);
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;
+ ret_dentry = ERR_PTR(-EXDEV);
goto out;
}
@@ -196,14 +194,54 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
/* inherit lower inode number for sdcardfs's inode */
inode = sdcardfs_iget(sb, lower_inode, id);
if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
+ ret_dentry = ERR_CAST(inode);
goto out;
}
- d_add(dentry, inode);
+ ret_dentry = d_splice_alias(inode, dentry);
+ dentry = ret_dentry ?: dentry;
update_derived_permission_lock(dentry);
out:
- return err;
+ return ret_dentry;
+}
+
+/*
+ * Connect an 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)
+{
+ struct dentry *ret_dentry;
+
+ ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id);
+ return PTR_ERR(ret_dentry);
+}
+
+struct sdcardfs_name_data {
+ struct dir_context ctx;
+ const struct qstr *to_find;
+ char *name;
+ bool found;
+};
+
+static int sdcardfs_name_match(struct dir_context *ctx, const char *name,
+ int namelen, loff_t offset, u64 ino, unsigned int d_type)
+{
+ struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx);
+ struct qstr candidate = QSTR_INIT(name, namelen);
+
+ if (qstr_case_eq(buf->to_find, &candidate)) {
+ memcpy(buf->name, name, namelen);
+ buf->name[namelen] = 0;
+ buf->found = true;
+ return 1;
+ }
+ return 0;
}
/*
@@ -221,6 +259,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
struct dentry *lower_dentry;
const struct qstr *name;
struct path lower_path;
+ struct qstr dname;
+ struct dentry *ret_dentry = NULL;
struct sdcardfs_sb_info *sbi;
sbi = SDCARDFS_SB(dentry->d_sb);
@@ -241,60 +281,79 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
&lower_path);
/* check for other cases */
if (err == -ENOENT) {
- struct dentry *child;
- struct dentry *match = NULL;
- mutex_lock(&d_inode(lower_dir_dentry)->i_mutex);
- spin_lock(&lower_dir_dentry->d_lock);
- list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) {
- if (child && d_inode(child)) {
- if (qstr_case_eq(&child->d_name, name)) {
- match = dget(child);
- break;
- }
- }
+ struct file *file;
+ const struct cred *cred = current_cred();
+
+ struct sdcardfs_name_data buffer = {
+ .ctx.actor = sdcardfs_name_match,
+ .to_find = name,
+ .name = __getname(),
+ .found = false,
+ };
+
+ if (!buffer.name) {
+ err = -ENOMEM;
+ goto out;
+ }
+ file = dentry_open(lower_parent_path, O_RDONLY, cred);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto put_name;
}
- spin_unlock(&lower_dir_dentry->d_lock);
- mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex);
- if (match) {
+ err = iterate_dir(file, &buffer.ctx);
+ fput(file);
+ if (err)
+ goto put_name;
+
+ if (buffer.found)
err = vfs_path_lookup(lower_dir_dentry,
lower_dir_mnt,
- match->d_name.name, 0,
+ buffer.name, 0,
&lower_path);
- dput(match);
- }
+ else
+ err = -ENOENT;
+put_name:
+ __putname(buffer.name);
}
/* 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 */
+ * the inode of the graft path
+ */
- if(need_graft_path(dentry)) {
+ if (need_graft_path(dentry)) {
/* setup_obb_dentry()
- * The lower_path will be stored to the dentry's orig_path
+ * 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) */
+ * returns: -ERRNO if error (0: no error)
+ */
err = setup_obb_dentry(dentry, &lower_path);
- if(err) {
+ 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");
+ * a lookup fail.
+ */
+ pr_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 */
+ ret_dentry =
+ __sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
+ if (IS_ERR(ret_dentry)) {
+ err = PTR_ERR(ret_dentry);
+ /* path_put underlying path on error */
sdcardfs_put_reset_lower_path(dentry);
+ }
goto out;
}
@@ -306,11 +365,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
goto out;
/* instatiate a new negative dentry */
- lower_dentry = d_lookup(lower_dir_dentry, name);
+ dname.name = name->name;
+ dname.len = name->len;
+ dname.hash = full_name_hash(dname.name, dname.len);
+ lower_dentry = d_lookup(lower_dir_dentry, &dname);
if (lower_dentry)
goto setup_lower;
- lower_dentry = d_alloc(lower_dir_dentry, name);
+ lower_dentry = d_alloc(lower_dir_dentry, &dname);
if (!lower_dentry) {
err = -ENOMEM;
goto out;
@@ -331,14 +393,16 @@ setup_lower:
err = 0;
out:
- return ERR_PTR(err);
+ if (err)
+ return ERR_PTR(err);
+ return ret_dentry;
}
/*
* On success:
- * fills dentry object appropriate values and returns NULL.
+ * fills dentry object appropriate values and returns NULL.
* On fail (== error)
- * returns error ptr
+ * returns error ptr
*
* @dir : Parent inode. It is locked (dir->i_mutex)
* @dentry : Target dentry to lookup. we should set each of fields.
@@ -355,13 +419,10 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
+ if (!check_caller_access_to_name(d_inode(parent), &dentry->d_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_I(dir));
@@ -377,9 +438,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid);
if (IS_ERR(ret))
- {
goto out;
- }
if (ret)
dentry = ret;
if (d_inode(dentry)) {
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 7a8eae29e44d..953d2156d2e9 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -29,7 +29,7 @@ enum {
Opt_gid,
Opt_debug,
Opt_mask,
- Opt_multiuser, // May need?
+ Opt_multiuser,
Opt_userid,
Opt_reserved_mb,
Opt_err,
@@ -72,6 +72,7 @@ static int parse_options(struct super_block *sb, char *options, int silent,
while ((p = strsep(&options, ",")) != NULL) {
int token;
+
if (!*p)
continue;
@@ -116,19 +117,17 @@ static int parse_options(struct super_block *sb, char *options, int silent,
break;
/* unknown option */
default:
- if (!silent) {
- printk( KERN_ERR "Unrecognized mount option \"%s\" "
- "or missing value", p);
- }
+ if (!silent)
+ pr_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",
+ pr_info("sdcardfs : options - debug:%d\n", *debug);
+ pr_info("sdcardfs : options - uid:%d\n",
opts->fs_low_uid);
- printk( KERN_INFO "sdcardfs : options - gid:%d\n",
+ pr_info("sdcardfs : options - gid:%d\n",
opts->fs_low_gid);
}
@@ -148,6 +147,7 @@ int parse_options_remount(struct super_block *sb, char *options, int silent,
while ((p = strsep(&options, ",")) != NULL) {
int token;
+
if (!*p)
continue;
@@ -173,22 +173,20 @@ int parse_options_remount(struct super_block *sb, char *options, int silent,
case Opt_fsuid:
case Opt_fsgid:
case Opt_reserved_mb:
- printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p);
+ pr_warn("Option \"%s\" can't be changed during remount\n", p);
break;
/* unknown option */
default:
- if (!silent) {
- printk( KERN_ERR "Unrecognized mount option \"%s\" "
- "or missing value", p);
- }
+ if (!silent)
+ pr_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 - gid:%d\n", vfsopts->gid);
- printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask);
+ pr_info("sdcardfs : options - debug:%d\n", debug);
+ pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid);
+ pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask);
}
return 0;
@@ -223,8 +221,8 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb)
#endif
DEFINE_MUTEX(sdcardfs_super_list_lock);
-LIST_HEAD(sdcardfs_super_list);
EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock);
+LIST_HEAD(sdcardfs_super_list);
EXPORT_SYMBOL_GPL(sdcardfs_super_list);
/*
@@ -242,31 +240,30 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
struct inode *inode;
- printk(KERN_INFO "sdcardfs version 2.0\n");
+ pr_info("sdcardfs version 2.0\n");
if (!dev_name) {
- printk(KERN_ERR
- "sdcardfs: read_super: missing dev_name argument\n");
+ pr_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);
- printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt);
+ pr_info("sdcardfs: dev_name -> %s\n", dev_name);
+ pr_info("sdcardfs: options -> %s\n", (char *)raw_data);
+ pr_info("sdcardfs: mnt -> %p\n", mnt);
/* 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);
+ pr_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");
+ pr_crit("sdcardfs: read_super: out of memory\n");
err = -ENOMEM;
goto out_free;
}
@@ -275,7 +272,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
/* parse options */
err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options);
if (err) {
- printk(KERN_ERR "sdcardfs: invalid options\n");
+ pr_err("sdcardfs: invalid options\n");
goto out_freesbi;
}
@@ -328,14 +325,15 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
/* 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(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
+ if (sb_info->options.multiuser) {
+ setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT,
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, d_inode(sb->s_root));
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(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
+ setup_derived_state(d_inode(sb->s_root), PERM_ROOT,
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, d_inode(sb->s_root));
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
fixup_tmp_permissions(d_inode(sb->s_root));
@@ -344,7 +342,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
mutex_unlock(&sdcardfs_super_list_lock);
if (!silent)
- printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n",
+ pr_info("sdcardfs: mounted on top of %s type %s\n",
dev_name, lower_sb->s_type->name);
goto out; /* all is well */
@@ -368,8 +366,10 @@ out:
/* A feature which supports mount_nodev() with options */
static struct dentry *mount_nodev_with_options(struct vfsmount *mnt,
- struct file_system_type *fs_type, int flags, const char *dev_name, void *data,
- int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int))
+ struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data,
+ int (*fill_super)(struct vfsmount *, struct super_block *,
+ const char *, void *, int))
{
int error;
@@ -401,19 +401,22 @@ static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
raw_data, sdcardfs_read_super);
}
-static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *raw_data)
+static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *raw_data)
{
WARN(1, "sdcardfs does not support mount. Use mount2.\n");
return ERR_PTR(-EINVAL);
}
-void *sdcardfs_alloc_mnt_data(void) {
+void *sdcardfs_alloc_mnt_data(void)
+{
return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
}
-void sdcardfs_kill_sb(struct super_block *sb) {
+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);
@@ -432,6 +435,7 @@ static struct file_system_type sdcardfs_fs_type = {
.kill_sb = sdcardfs_kill_sb,
.fs_flags = 0,
};
+MODULE_ALIAS_FS(SDCARDFS_NAME);
static int __init init_sdcardfs_fs(void)
{
@@ -467,10 +471,15 @@ static void __exit exit_sdcardfs_fs(void)
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/)");
+/* Original wrapfs authors */
+MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)");
+
+/* Original sdcardfs authors */
+MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics");
+
+/* Current maintainer */
+MODULE_AUTHOR("Daniel Rosenberg, Google");
+MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION);
MODULE_LICENSE("GPL");
module_init(init_sdcardfs_fs);
diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c
index e21f64675a80..0d4089c62c3a 100644
--- a/fs/sdcardfs/mmap.c
+++ b/fs/sdcardfs/mmap.c
@@ -48,34 +48,55 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
return err;
}
+static int sdcardfs_page_mkwrite(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
+{
+ int err = 0;
+ 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);
+ if (!lower_vm_ops->page_mkwrite)
+ goto out;
+
+ lower_file = sdcardfs_lower_file(file);
+ /*
+ * XXX: vm_ops->page_mkwrite 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_page_mkwrite 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
+ * ->page_mkwrite to take an explicit file pointer.
+ */
+ lower_vma.vm_file = lower_file;
+ err = lower_vm_ops->page_mkwrite(&lower_vma, vmf);
+out:
+ 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.
+ * This function should never be called directly. We need it
+ * to exist, to get past a check in open_check_o_direct(),
+ * which is called from do_last().
*/
- printk(KERN_INFO "%s, operation is not supported\n", __func__);
- return 0;
+ return -EINVAL;
}
-/*
- * 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,
+ .page_mkwrite = sdcardfs_page_mkwrite,
};
diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h
index 52bc20080904..2e89b5872314 100644
--- a/fs/sdcardfs/multiuser.h
+++ b/fs/sdcardfs/multiuser.h
@@ -28,22 +28,17 @@
typedef uid_t userid_t;
typedef uid_t appid_t;
-static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
- return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
+static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id)
+{
+ return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
-static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
- if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
- return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
- } else {
- return -1;
- }
+static inline gid_t multiuser_get_cache_gid(uid_t uid)
+{
+ return uid - AID_APP_START + AID_CACHE_GID_START;
}
-static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
- if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
- return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
- } else {
- return -1;
- }
+static inline gid_t multiuser_get_ext_gid(uid_t uid)
+{
+ return uid - AID_APP_START + AID_EXT_GID_START;
}
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index d96fcde041cc..89196e31073e 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -20,6 +20,7 @@
#include "sdcardfs.h"
#include <linux/hashtable.h>
+#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/radix-tree.h>
#include <linux/dcache.h>
@@ -44,13 +45,24 @@ static DEFINE_HASHTABLE(ext_to_groupid, 8);
static struct kmem_cache *hashtable_entry_cachep;
-static void inline qstr_init(struct qstr *q, const char *name) {
+static unsigned int full_name_case_hash(const unsigned char *name, unsigned int len)
+{
+ unsigned long hash = init_name_hash();
+
+ while (len--)
+ hash = partial_name_hash(tolower(*name++), hash);
+ return end_name_hash(hash);
+}
+
+static inline void qstr_init(struct qstr *q, const char *name)
+{
q->name = name;
q->len = strlen(q->name);
- q->hash = full_name_hash(q->name, q->len);
+ q->hash = full_name_case_hash(q->name, q->len);
}
-static inline int qstr_copy(const struct qstr *src, struct qstr *dest) {
+static inline int qstr_copy(const struct qstr *src, struct qstr *dest)
+{
dest->name = kstrdup(src->name, GFP_KERNEL);
dest->hash_len = src->hash_len;
return !!dest->name;
@@ -78,6 +90,7 @@ static appid_t __get_appid(const struct qstr *key)
appid_t get_appid(const char *key)
{
struct qstr q;
+
qstr_init(&q, key);
return __get_appid(&q);
}
@@ -103,6 +116,7 @@ static appid_t __get_ext_gid(const struct qstr *key)
appid_t get_ext_gid(const char *key)
{
struct qstr q;
+
qstr_init(&q, key);
return __get_ext_gid(&q);
}
@@ -133,8 +147,10 @@ appid_t is_excluded(const char *key, userid_t user)
/* 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 struct qstr *name) {
+ * even further, such as enforcing that apps hold sdcard_rw.
+ */
+int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name)
+{
struct qstr q_autorun = QSTR_LITERAL("autorun.inf");
struct qstr q__android_secure = QSTR_LITERAL(".android_secure");
struct qstr q_android_secure = QSTR_LITERAL("android_secure");
@@ -149,26 +165,26 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na
}
/* 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) {
+ * 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) {
+ * 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) {
+ if ((open_flags & O_ACCMODE) == O_WRONLY)
return 1; /* W_OK */
- } else {
- /* Probably O_RDRW, but treat as default to be safe */
+ /* Probably O_RDRW, but treat as default to be safe */
return 1; /* R_OK | W_OK */
- }
}
static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key,
@@ -178,6 +194,8 @@ static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key,
GFP_KERNEL);
if (!ret)
return NULL;
+ INIT_HLIST_NODE(&ret->dlist);
+ INIT_HLIST_NODE(&ret->hlist);
if (!qstr_copy(key, &ret->key)) {
kmem_cache_free(hashtable_entry_cachep, ret);
@@ -249,8 +267,7 @@ static void fixup_all_perms_name(const struct qstr *key)
struct sdcardfs_sb_info *sbinfo;
struct limit_search limit = {
.flags = BY_NAME,
- .name = key->name,
- .length = key->len,
+ .name = QSTR_INIT(key->name, key->len),
};
list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
if (sbinfo_has_sdcard_magic(sbinfo))
@@ -263,8 +280,7 @@ static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid)
struct sdcardfs_sb_info *sbinfo;
struct limit_search limit = {
.flags = BY_NAME | BY_USERID,
- .name = key->name,
- .length = key->len,
+ .name = QSTR_INIT(key->name, key->len),
.userid = userid,
};
list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
@@ -326,7 +342,6 @@ static int insert_userid_exclude_entry(const struct qstr *key, userid_t value)
static void free_hashtable_entry(struct hashtable_entry *entry)
{
kfree(entry->key.name);
- hash_del_rcu(&entry->dlist);
kmem_cache_free(hashtable_entry_cachep, entry);
}
@@ -361,7 +376,6 @@ static void remove_packagelist_entry(const struct qstr *key)
remove_packagelist_entry_locked(key);
fixup_all_perms_name(key);
mutex_unlock(&sdcardfs_super_list_lock);
- return;
}
static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group)
@@ -384,7 +398,6 @@ static void remove_ext_gid_entry(const struct qstr *key, gid_t group)
mutex_lock(&sdcardfs_super_list_lock);
remove_ext_gid_entry_locked(key, group);
mutex_unlock(&sdcardfs_super_list_lock);
- return;
}
static void remove_userid_all_entry_locked(userid_t userid)
@@ -412,7 +425,6 @@ static void remove_userid_all_entry(userid_t userid)
remove_userid_all_entry_locked(userid);
fixup_all_perms_userid(userid);
mutex_unlock(&sdcardfs_super_list_lock);
- return;
}
static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid)
@@ -437,7 +449,6 @@ static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid)
remove_userid_exclude_entry_locked(key, userid);
fixup_all_perms_name_userid(key, userid);
mutex_unlock(&sdcardfs_super_list_lock);
- return;
}
static void packagelist_destroy(void)
@@ -446,6 +457,7 @@ static void packagelist_destroy(void)
struct hlist_node *h_t;
HLIST_HEAD(free_list);
int i;
+
mutex_lock(&sdcardfs_super_list_lock);
hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
hash_del_rcu(&hash_cur->hlist);
@@ -459,7 +471,7 @@ static void packagelist_destroy(void)
hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
free_hashtable_entry(hash_cur);
mutex_unlock(&sdcardfs_super_list_lock);
- printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
+ pr_info("sdcardfs: destroyed packagelist pkgld\n");
}
#define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \
@@ -575,7 +587,8 @@ static ssize_t package_details_clear_userid_store(struct config_item *item,
static void package_details_release(struct config_item *item)
{
struct package_details *package_details = to_package_details(item);
- printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name);
+
+ pr_info("sdcardfs: removing %s\n", package_details->name.name);
remove_packagelist_entry(&package_details->name);
kfree(package_details->name.name);
kfree(package_details);
@@ -593,7 +606,7 @@ static struct configfs_attribute *package_details_attrs[] = {
};
static struct configfs_item_operations package_details_item_ops = {
- .release = package_details_release,
+ .release = package_details_release,
};
static struct config_item_type package_appid_type = {
@@ -627,7 +640,7 @@ static void extension_details_release(struct config_item *item)
{
struct extension_details *extension_details = to_extension_details(item);
- printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n",
+ pr_info("sdcardfs: No longer mapping %s files to gid %d\n",
extension_details->name.name, extension_details->num);
remove_ext_gid_entry(&extension_details->name, extension_details->num);
kfree(extension_details->name.name);
@@ -649,6 +662,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou
struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL);
const char *tmp;
int ret;
+
if (!extension_details)
return ERR_PTR(-ENOMEM);
@@ -703,7 +717,8 @@ static struct config_group *extensions_make_group(struct config_group *group, co
static void extensions_drop_group(struct config_group *group, struct config_item *item)
{
struct extensions_value *value = to_extensions_value(item);
- printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num);
+
+ pr_info("sdcardfs: No longer mapping any files to gid %d\n", value->num);
kfree(value);
}
@@ -837,14 +852,14 @@ static int configfs_sdcardfs_init(void)
{
int ret, i;
struct configfs_subsystem *subsys = &sdcardfs_packages;
- for (i = 0; sd_default_groups[i]; i++) {
+
+ for (i = 0; sd_default_groups[i]; i++)
config_group_init(sd_default_groups[i]);
- }
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",
+ pr_err("Error %d while registering subsystem %s\n",
ret,
subsys->su_group.cg_item.ci_namebuf);
}
@@ -862,18 +877,17 @@ int packagelist_init(void)
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");
+ pr_err("sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
return -ENOMEM;
}
configfs_sdcardfs_init();
- return 0;
+ return 0;
}
void packagelist_exit(void)
{
configfs_sdcardfs_exit();
packagelist_destroy();
- if (hashtable_entry_cachep)
- kmem_cache_destroy(hashtable_entry_cachep);
+ kmem_cache_destroy(hashtable_entry_cachep);
}
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index f3cced313108..2b67b9a8ef9f 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -29,6 +29,7 @@
#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/aio.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <linux/namei.h>
@@ -52,7 +53,7 @@
#define SDCARDFS_ROOT_INO 1
/* useful for tracking code reachability */
-#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
+#define UDBG pr_default("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
#define SDCARDFS_DIRENT_SIZE 256
@@ -86,54 +87,56 @@
} while (0)
/* OVERRIDE_CRED() and REVERT_CRED()
- * OVERRID_CRED()
- * backup original task->cred
- * and modifies task->cred->fsuid/fsgid to specified value.
+ * OVERRIDE_CRED()
+ * backup original task->cred
+ * and modifies task->cred->fsuid/fsgid to specified value.
* REVERT_CRED()
- * restore original task->cred->fsuid/fsgid.
+ * 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, info) \
+ do { \
saved_cred = override_fsids(sdcardfs_sbi, info); \
- if (!saved_cred) { return -ENOMEM; }
+ if (!saved_cred) \
+ return -ENOMEM; \
+ } while (0)
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
+ do { \
saved_cred = override_fsids(sdcardfs_sbi, info); \
- if (!saved_cred) { return ERR_PTR(-ENOMEM); }
+ if (!saved_cred) \
+ return ERR_PTR(-ENOMEM); \
+ } while (0)
#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. */
+ * 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,
- /* This node is "/Android/[data|media|obb]/[package]" */
- PERM_ANDROID_PACKAGE,
- /* This node is "/Android/[data|media|obb]/[package]/cache" */
- PERM_ANDROID_PACKAGE_CACHE,
+ /* 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,
+ /* This node is "/Android/[data|media|obb]/[package]" */
+ PERM_ANDROID_PACKAGE,
+ /* This node is "/Android/[data|media|obb]/[package]/cache" */
+ PERM_ANDROID_PACKAGE_CACHE,
} perm_t;
struct sdcardfs_sb_info;
@@ -141,9 +144,9 @@ struct sdcardfs_mount_options;
struct sdcardfs_inode_info;
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info);
+const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info);
/* Do not directly use this function, use REVERT_CRED() instead. */
-void revert_fsids(const struct cred * old_cred);
+void revert_fsids(const struct cred *old_cred);
/* operations vectors defined in specific files */
extern const struct file_operations sdcardfs_main_fops;
@@ -220,7 +223,8 @@ 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) */
+ * to sdcardfs_mount_options (Android 4.4 support)
+ */
struct sdcardfs_mount_options options;
spinlock_t lock; /* protects obbpath */
char *obbpath_s;
@@ -331,7 +335,7 @@ 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) { \
+ if (SDCARDFS_D(dent)->pname.dentry) { \
pathcpy(&pname, &SDCARDFS_D(dent)->pname); \
SDCARDFS_D(dent)->pname.dentry = NULL; \
SDCARDFS_D(dent)->pname.mnt = NULL; \
@@ -347,17 +351,17 @@ SDCARDFS_DENT_FUNC(orig_path)
static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
{
- return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
+ return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
}
/* grab a refererence if we aren't linking to ourself */
static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
{
struct inode *old_top = NULL;
+
BUG_ON(IS_ERR_OR_NULL(top));
- if (info->top && info->top != &info->vfs_inode) {
+ if (info->top && info->top != &info->vfs_inode)
old_top = info->top;
- }
if (top != &info->vfs_inode)
igrab(top);
info->top = top;
@@ -367,11 +371,11 @@ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
static inline struct inode *grab_top(struct sdcardfs_inode_info *info)
{
struct inode *top = info->top;
- if (top) {
+
+ if (top)
return igrab(top);
- } else {
+ else
return NULL;
- }
}
static inline void release_top(struct sdcardfs_inode_info *info)
@@ -379,21 +383,24 @@ static inline void release_top(struct sdcardfs_inode_info *info)
iput(info->top);
}
-static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
+static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info)
+{
struct sdcardfs_vfsmount_options *opts = mnt->data;
- if (opts->gid == AID_SDCARD_RW) {
+ if (opts->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. */
+ * assigned to app directories are still multiuser aware.
+ */
return AID_SDCARD_RW;
- } else {
+ else
return multiuser_get_uid(info->userid, opts->gid);
- }
}
-static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
+
+static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info)
+{
int owner_mode;
int filtered_mode;
struct sdcardfs_vfsmount_options *opts = mnt->data;
@@ -402,17 +409,18 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf
if (info->perm == PERM_PRE_ROOT) {
/* Top of multi-user view should always be visible to ensure
- * secondary users can traverse inside. */
+ * 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 (opts->gid == AID_SDCARD_RW) {
+ * leave +x open for the default view.
+ */
+ if (opts->gid == AID_SDCARD_RW)
visible_mode = visible_mode & ~0006;
- } else {
+ 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));
@@ -437,7 +445,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent,
/* in case of a local obb dentry
* the orig_path should be returned
*/
- if(has_graft_path(dent))
+ if (has_graft_path(dent))
sdcardfs_get_orig_path(dent, real_lower);
else
sdcardfs_get_lower_path(dent, real_lower);
@@ -446,7 +454,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent,
static inline void sdcardfs_put_real_lower(const struct dentry *dent,
struct path *real_lower)
{
- if(has_graft_path(dent))
+ if (has_graft_path(dent))
sdcardfs_put_orig_path(dent, real_lower);
else
sdcardfs_put_lower_path(dent, real_lower);
@@ -459,7 +467,7 @@ extern struct list_head sdcardfs_super_list;
extern appid_t get_appid(const char *app_name);
extern appid_t get_ext_gid(const char *app_name);
extern appid_t is_excluded(const char *app_name, userid_t userid);
-extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr* name);
+extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name);
extern int open_flags_to_access_mode(int open_flags);
extern int packagelist_init(void);
extern void packagelist_exit(void);
@@ -469,8 +477,7 @@ extern void packagelist_exit(void);
#define BY_USERID (1 << 1)
struct limit_search {
unsigned int flags;
- const char *name;
- size_t length;
+ struct qstr name;
userid_t userid;
};
@@ -478,12 +485,10 @@ extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t useri
uid_t uid, bool under_android, struct inode *top);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
-extern void drop_recursive(struct dentry *parent);
-extern void fixup_top_recursive(struct dentry *parent);
extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
extern void update_derived_permission_lock(struct dentry *dentry);
-void fixup_lower_ownership(struct dentry* dentry, const char *name);
+void fixup_lower_ownership(struct dentry *dentry, const char *name);
extern int need_graft_path(struct dentry *dentry);
extern int is_base_obbpath(struct dentry *dentry);
extern int is_obbpath_invalid(struct dentry *dentry);
@@ -493,6 +498,7 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path);
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;
}
@@ -611,6 +617,11 @@ static inline bool str_case_eq(const char *s1, const char *s2)
return !strcasecmp(s1, s2);
}
+static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len)
+{
+ return !strncasecmp(s1, s2, len);
+}
+
static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2)
{
return q1->len == q2->len && str_case_eq(q1->name, q2->name);
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index edda32b68dc0..a3393e959c63 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -36,7 +36,7 @@ static void sdcardfs_put_super(struct super_block *sb)
if (!spd)
return;
- if(spd->obbpath_s) {
+ if (spd->obbpath_s) {
kfree(spd->obbpath_s);
path_put(&spd->obbpath);
}
@@ -64,7 +64,7 @@ static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf)
if (sbi->options.reserved_mb) {
/* Invalid statfs informations. */
if (buf->f_bsize == 0) {
- printk(KERN_ERR "Returned block size is zero.\n");
+ pr_err("Returned block size is zero.\n");
return -EINVAL;
}
@@ -100,8 +100,7 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options
* 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);
+ pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags);
err = -EINVAL;
}
@@ -125,29 +124,33 @@ static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb,
* SILENT, but anything else left over is an error.
*/
if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) {
- printk(KERN_ERR
- "sdcardfs: remount flags 0x%x unsupported\n", *flags);
+ pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags);
err = -EINVAL;
}
- printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt);
+ pr_info("Remount options were %s for vfsmnt %p.\n", options, mnt);
err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data);
return err;
}
-static void* sdcardfs_clone_mnt_data(void *data) {
- struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
- struct sdcardfs_vfsmount_options* old = data;
- if(!opt) return NULL;
+static void *sdcardfs_clone_mnt_data(void *data)
+{
+ struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
+ struct sdcardfs_vfsmount_options *old = data;
+
+ if (!opt)
+ return NULL;
opt->gid = old->gid;
opt->mask = old->mask;
return opt;
}
-static void sdcardfs_copy_mnt_data(void *data, void *newdata) {
- struct sdcardfs_vfsmount_options* old = data;
- struct sdcardfs_vfsmount_options* new = newdata;
+static void sdcardfs_copy_mnt_data(void *data, void *newdata)
+{
+ struct sdcardfs_vfsmount_options *old = data;
+ struct sdcardfs_vfsmount_options *new = newdata;
+
old->gid = new->gid;
old->mask = new->mask;
}
@@ -218,8 +221,7 @@ int sdcardfs_init_inode_cache(void)
/* sdcardfs inode cache destructor */
void sdcardfs_destroy_inode_cache(void)
{
- if (sdcardfs_inode_cachep)
- kmem_cache_destroy(sdcardfs_inode_cachep);
+ kmem_cache_destroy(sdcardfs_inode_cachep);
}
/*
@@ -235,7 +237,8 @@ static void sdcardfs_umount_begin(struct super_block *sb)
lower_sb->s_op->umount_begin(lower_sb);
}
-static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root)
+static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m,
+ struct dentry *root)
{
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
struct sdcardfs_mount_options *opts = &sbi->options;
@@ -248,7 +251,7 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struc
if (vfsopts->gid != 0)
seq_printf(m, ",gid=%u", vfsopts->gid);
if (opts->multiuser)
- seq_printf(m, ",multiuser");
+ seq_puts(m, ",multiuser");
if (vfsopts->mask)
seq_printf(m, ",mask=%u", vfsopts->mask);
if (opts->fs_user_id)
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
index ffb093e72b6c..6dd158a216f4 100644
--- a/fs/squashfs/Kconfig
+++ b/fs/squashfs/Kconfig
@@ -26,34 +26,6 @@ config SQUASHFS
If unsure, say N.
choice
- prompt "File decompression options"
- depends on SQUASHFS
- help
- Squashfs now supports two options for decompressing file
- data. Traditionally Squashfs has decompressed into an
- intermediate buffer and then memcopied it into the page cache.
- Squashfs now supports the ability to decompress directly into
- the page cache.
-
- If unsure, select "Decompress file data into an intermediate buffer"
-
-config SQUASHFS_FILE_CACHE
- bool "Decompress file data into an intermediate buffer"
- help
- Decompress file data into an intermediate buffer and then
- memcopy it into the page cache.
-
-config SQUASHFS_FILE_DIRECT
- bool "Decompress files directly into the page cache"
- help
- Directly decompress file data into the page cache.
- Doing so can significantly improve performance because
- it eliminates a memcpy and it also removes the lock contention
- on the single buffer.
-
-endchoice
-
-choice
prompt "Decompressor parallelisation options"
depends on SQUASHFS
help
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
index 246a6f329d89..fe51f1507ed1 100644
--- a/fs/squashfs/Makefile
+++ b/fs/squashfs/Makefile
@@ -5,8 +5,7 @@
obj-$(CONFIG_SQUASHFS) += squashfs.o
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
squashfs-y += namei.o super.o symlink.o decompressor.o
-squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o
-squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o
+squashfs-y += file_direct.o page_actor.o
squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index 0cea9b9236d0..2eb66decc5ab 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -28,9 +28,12 @@
#include <linux/fs.h>
#include <linux/vfs.h>
+#include <linux/bio.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/pagemap.h>
#include <linux/buffer_head.h>
+#include <linux/workqueue.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -38,177 +41,434 @@
#include "decompressor.h"
#include "page_actor.h"
-/*
- * Read the metadata block length, this is stored in the first two
- * bytes of the metadata block.
- */
-static struct buffer_head *get_block_length(struct super_block *sb,
- u64 *cur_index, int *offset, int *length)
+static struct workqueue_struct *squashfs_read_wq;
+
+struct squashfs_read_request {
+ struct super_block *sb;
+ u64 index;
+ int length;
+ int compressed;
+ int offset;
+ u64 read_end;
+ struct squashfs_page_actor *output;
+ enum {
+ SQUASHFS_COPY,
+ SQUASHFS_DECOMPRESS,
+ SQUASHFS_METADATA,
+ } data_processing;
+ bool synchronous;
+
+ /*
+ * If the read is synchronous, it is possible to retrieve information
+ * about the request by setting these pointers.
+ */
+ int *res;
+ int *bytes_read;
+ int *bytes_uncompressed;
+
+ int nr_buffers;
+ struct buffer_head **bh;
+ struct work_struct offload;
+};
+
+struct squashfs_bio_request {
+ struct buffer_head **bh;
+ int nr_buffers;
+};
+
+static int squashfs_bio_submit(struct squashfs_read_request *req);
+
+int squashfs_init_read_wq(void)
{
- struct squashfs_sb_info *msblk = sb->s_fs_info;
- struct buffer_head *bh;
+ squashfs_read_wq = create_workqueue("SquashFS read wq");
+ return !!squashfs_read_wq;
+}
+
+void squashfs_destroy_read_wq(void)
+{
+ flush_workqueue(squashfs_read_wq);
+ destroy_workqueue(squashfs_read_wq);
+}
+
+static void free_read_request(struct squashfs_read_request *req, int error)
+{
+ if (!req->synchronous)
+ squashfs_page_actor_free(req->output, error);
+ if (req->res)
+ *(req->res) = error;
+ kfree(req->bh);
+ kfree(req);
+}
+
+static void squashfs_process_blocks(struct squashfs_read_request *req)
+{
+ int error = 0;
+ int bytes, i, length;
+ struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+ struct squashfs_page_actor *actor = req->output;
+ struct buffer_head **bh = req->bh;
+ int nr_buffers = req->nr_buffers;
+
+ for (i = 0; i < nr_buffers; ++i) {
+ if (!bh[i])
+ continue;
+ wait_on_buffer(bh[i]);
+ if (!buffer_uptodate(bh[i]))
+ error = -EIO;
+ }
+ if (error)
+ goto cleanup;
+
+ if (req->data_processing == SQUASHFS_METADATA) {
+ /* Extract the length of the metadata block */
+ if (req->offset != msblk->devblksize - 1)
+ length = *((u16 *)(bh[0]->b_data + req->offset));
+ else {
+ length = bh[0]->b_data[req->offset];
+ length |= bh[1]->b_data[0] << 8;
+ }
+ req->compressed = SQUASHFS_COMPRESSED(length);
+ req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS
+ : SQUASHFS_COPY;
+ length = SQUASHFS_COMPRESSED_SIZE(length);
+ if (req->index + length + 2 > req->read_end) {
+ for (i = 0; i < nr_buffers; ++i)
+ put_bh(bh[i]);
+ kfree(bh);
+ req->length = length;
+ req->index += 2;
+ squashfs_bio_submit(req);
+ return;
+ }
+ req->length = length;
+ req->offset = (req->offset + 2) % PAGE_SIZE;
+ if (req->offset < 2) {
+ put_bh(bh[0]);
+ ++bh;
+ --nr_buffers;
+ }
+ }
+ if (req->bytes_read)
+ *(req->bytes_read) = req->length;
- bh = sb_bread(sb, *cur_index);
- if (bh == NULL)
- return NULL;
-
- if (msblk->devblksize - *offset == 1) {
- *length = (unsigned char) bh->b_data[*offset];
- put_bh(bh);
- bh = sb_bread(sb, ++(*cur_index));
- if (bh == NULL)
- return NULL;
- *length |= (unsigned char) bh->b_data[0] << 8;
- *offset = 1;
- } else {
- *length = (unsigned char) bh->b_data[*offset] |
- (unsigned char) bh->b_data[*offset + 1] << 8;
- *offset += 2;
-
- if (*offset == msblk->devblksize) {
- put_bh(bh);
- bh = sb_bread(sb, ++(*cur_index));
- if (bh == NULL)
- return NULL;
- *offset = 0;
+ if (req->data_processing == SQUASHFS_COPY) {
+ squashfs_bh_to_actor(bh, nr_buffers, req->output, req->offset,
+ req->length, msblk->devblksize);
+ } else if (req->data_processing == SQUASHFS_DECOMPRESS) {
+ req->length = squashfs_decompress(msblk, bh, nr_buffers,
+ req->offset, req->length, actor);
+ if (req->length < 0) {
+ error = -EIO;
+ goto cleanup;
}
}
- return bh;
+ /* Last page may have trailing bytes not filled */
+ bytes = req->length % PAGE_SIZE;
+ if (bytes && actor->page[actor->pages - 1])
+ zero_user_segment(actor->page[actor->pages - 1], bytes,
+ PAGE_SIZE);
+
+cleanup:
+ if (req->bytes_uncompressed)
+ *(req->bytes_uncompressed) = req->length;
+ if (error) {
+ for (i = 0; i < nr_buffers; ++i)
+ if (bh[i])
+ put_bh(bh[i]);
+ }
+ free_read_request(req, error);
}
+static void read_wq_handler(struct work_struct *work)
+{
+ squashfs_process_blocks(container_of(work,
+ struct squashfs_read_request, offload));
+}
-/*
- * Read and decompress a metadata block or datablock. Length is non-zero
- * if a datablock is being read (the size is stored elsewhere in the
- * filesystem), otherwise the length is obtained from the first two bytes of
- * the metadata block. A bit in the length field indicates if the block
- * is stored uncompressed in the filesystem (usually because compression
- * generated a larger block - this does occasionally happen with compression
- * algorithms).
- */
-int squashfs_read_data(struct super_block *sb, u64 index, int length,
- u64 *next_index, struct squashfs_page_actor *output)
+static void squashfs_bio_end_io(struct bio *bio)
{
- struct squashfs_sb_info *msblk = sb->s_fs_info;
- struct buffer_head **bh;
- int offset = index & ((1 << msblk->devblksize_log2) - 1);
- u64 cur_index = index >> msblk->devblksize_log2;
- int bytes, compressed, b = 0, k = 0, avail, i;
+ int i;
+ int error = bio->bi_error;
+ struct squashfs_bio_request *bio_req = bio->bi_private;
+
+ bio_put(bio);
+
+ for (i = 0; i < bio_req->nr_buffers; ++i) {
+ if (!bio_req->bh[i])
+ continue;
+ if (!error)
+ set_buffer_uptodate(bio_req->bh[i]);
+ else
+ clear_buffer_uptodate(bio_req->bh[i]);
+ unlock_buffer(bio_req->bh[i]);
+ }
+ kfree(bio_req);
+}
+
+static int bh_is_optional(struct squashfs_read_request *req, int idx)
+{
+ int start_idx, end_idx;
+ struct squashfs_sb_info *msblk = req->sb->s_fs_info;
- bh = kcalloc(((output->length + msblk->devblksize - 1)
- >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
- if (bh == NULL)
+ start_idx = (idx * msblk->devblksize - req->offset) / PAGE_CACHE_SIZE;
+ end_idx = ((idx + 1) * msblk->devblksize - req->offset + 1) / PAGE_CACHE_SIZE;
+ if (start_idx >= req->output->pages)
+ return 1;
+ if (start_idx < 0)
+ start_idx = end_idx;
+ if (end_idx >= req->output->pages)
+ end_idx = start_idx;
+ return !req->output->page[start_idx] && !req->output->page[end_idx];
+}
+
+static int actor_getblks(struct squashfs_read_request *req, u64 block)
+{
+ int i;
+
+ req->bh = kmalloc_array(req->nr_buffers, sizeof(*(req->bh)), GFP_NOIO);
+ if (!req->bh)
return -ENOMEM;
- if (length) {
+ for (i = 0; i < req->nr_buffers; ++i) {
/*
- * Datablock.
+ * When dealing with an uncompressed block, the actor may
+ * contains NULL pages. There's no need to read the buffers
+ * associated with these pages.
*/
- bytes = -offset;
- compressed = SQUASHFS_COMPRESSED_BLOCK(length);
- length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
- if (next_index)
- *next_index = index + length;
-
- TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
- index, compressed ? "" : "un", length, output->length);
-
- if (length < 0 || length > output->length ||
- (index + length) > msblk->bytes_used)
- goto read_failure;
-
- for (b = 0; bytes < length; b++, cur_index++) {
- bh[b] = sb_getblk(sb, cur_index);
- if (bh[b] == NULL)
- goto block_release;
- bytes += msblk->devblksize;
+ if (!req->compressed && bh_is_optional(req, i)) {
+ req->bh[i] = NULL;
+ continue;
}
- ll_rw_block(READ, b, bh);
- } else {
- /*
- * Metadata block.
- */
- if ((index + 2) > msblk->bytes_used)
- goto read_failure;
+ req->bh[i] = sb_getblk(req->sb, block + i);
+ if (!req->bh[i]) {
+ while (--i) {
+ if (req->bh[i])
+ put_bh(req->bh[i]);
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
- bh[0] = get_block_length(sb, &cur_index, &offset, &length);
- if (bh[0] == NULL)
- goto read_failure;
- b = 1;
+static int squashfs_bio_submit(struct squashfs_read_request *req)
+{
+ struct bio *bio = NULL;
+ struct buffer_head *bh;
+ struct squashfs_bio_request *bio_req = NULL;
+ int b = 0, prev_block = 0;
+ struct squashfs_sb_info *msblk = req->sb->s_fs_info;
- bytes = msblk->devblksize - offset;
- compressed = SQUASHFS_COMPRESSED(length);
- length = SQUASHFS_COMPRESSED_SIZE(length);
- if (next_index)
- *next_index = index + length + 2;
+ u64 read_start = round_down(req->index, msblk->devblksize);
+ u64 read_end = round_up(req->index + req->length, msblk->devblksize);
+ sector_t block = read_start >> msblk->devblksize_log2;
+ sector_t block_end = read_end >> msblk->devblksize_log2;
+ int offset = read_start - round_down(req->index, PAGE_SIZE);
+ int nr_buffers = block_end - block;
+ int blksz = msblk->devblksize;
+ int bio_max_pages = nr_buffers > BIO_MAX_PAGES ? BIO_MAX_PAGES
+ : nr_buffers;
- TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
- compressed ? "" : "un", length);
+ /* Setup the request */
+ req->read_end = read_end;
+ req->offset = req->index - read_start;
+ req->nr_buffers = nr_buffers;
+ if (actor_getblks(req, block) < 0)
+ goto getblk_failed;
- if (length < 0 || length > output->length ||
- (index + length) > msblk->bytes_used)
- goto block_release;
+ /* Create and submit the BIOs */
+ for (b = 0; b < nr_buffers; ++b, offset += blksz) {
+ bh = req->bh[b];
+ if (!bh || !trylock_buffer(bh))
+ continue;
+ if (buffer_uptodate(bh)) {
+ unlock_buffer(bh);
+ continue;
+ }
+ offset %= PAGE_SIZE;
- for (; bytes < length; b++) {
- bh[b] = sb_getblk(sb, ++cur_index);
- if (bh[b] == NULL)
- goto block_release;
- bytes += msblk->devblksize;
+ /* Append the buffer to the current BIO if it is contiguous */
+ if (bio && bio_req && prev_block + 1 == b) {
+ if (bio_add_page(bio, bh->b_page, blksz, offset)) {
+ bio_req->nr_buffers += 1;
+ prev_block = b;
+ continue;
+ }
}
- ll_rw_block(READ, b - 1, bh + 1);
+
+ /* Otherwise, submit the current BIO and create a new one */
+ if (bio)
+ submit_bio(READ, bio);
+ bio_req = kcalloc(1, sizeof(struct squashfs_bio_request),
+ GFP_NOIO);
+ if (!bio_req)
+ goto req_alloc_failed;
+ bio_req->bh = &req->bh[b];
+ bio = bio_alloc(GFP_NOIO, bio_max_pages);
+ if (!bio)
+ goto bio_alloc_failed;
+ bio->bi_bdev = req->sb->s_bdev;
+ bio->bi_iter.bi_sector = (block + b)
+ << (msblk->devblksize_log2 - 9);
+ bio->bi_private = bio_req;
+ bio->bi_end_io = squashfs_bio_end_io;
+
+ bio_add_page(bio, bh->b_page, blksz, offset);
+ bio_req->nr_buffers += 1;
+ prev_block = b;
}
+ if (bio)
+ submit_bio(READ, bio);
- for (i = 0; i < b; i++) {
- wait_on_buffer(bh[i]);
- if (!buffer_uptodate(bh[i]))
- goto block_release;
+ if (req->synchronous)
+ squashfs_process_blocks(req);
+ else {
+ INIT_WORK(&req->offload, read_wq_handler);
+ schedule_work(&req->offload);
}
+ return 0;
- if (compressed) {
- length = squashfs_decompress(msblk, bh, b, offset, length,
- output);
- if (length < 0)
- goto read_failure;
- } else {
- /*
- * Block is uncompressed.
- */
- int in, pg_offset = 0;
- void *data = squashfs_first_page(output);
-
- for (bytes = length; k < b; k++) {
- in = min(bytes, msblk->devblksize - offset);
- bytes -= in;
- while (in) {
- if (pg_offset == PAGE_CACHE_SIZE) {
- data = squashfs_next_page(output);
- pg_offset = 0;
- }
- avail = min_t(int, in, PAGE_CACHE_SIZE -
- pg_offset);
- memcpy(data + pg_offset, bh[k]->b_data + offset,
- avail);
- in -= avail;
- pg_offset += avail;
- offset += avail;
- }
- offset = 0;
- put_bh(bh[k]);
- }
- squashfs_finish_page(output);
+bio_alloc_failed:
+ kfree(bio_req);
+req_alloc_failed:
+ unlock_buffer(bh);
+ while (--nr_buffers >= b)
+ if (req->bh[nr_buffers])
+ put_bh(req->bh[nr_buffers]);
+ while (--b >= 0)
+ if (req->bh[b])
+ wait_on_buffer(req->bh[b]);
+getblk_failed:
+ free_read_request(req, -ENOMEM);
+ return -ENOMEM;
+}
+
+static int read_metadata_block(struct squashfs_read_request *req,
+ u64 *next_index)
+{
+ int ret, error, bytes_read = 0, bytes_uncompressed = 0;
+ struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+
+ if (req->index + 2 > msblk->bytes_used) {
+ free_read_request(req, -EINVAL);
+ return -EINVAL;
+ }
+ req->length = 2;
+
+ /* Do not read beyond the end of the device */
+ if (req->index + req->length > msblk->bytes_used)
+ req->length = msblk->bytes_used - req->index;
+ req->data_processing = SQUASHFS_METADATA;
+
+ /*
+ * Reading metadata is always synchronous because we don't know the
+ * length in advance and the function is expected to update
+ * 'next_index' and return the length.
+ */
+ req->synchronous = true;
+ req->res = &error;
+ req->bytes_read = &bytes_read;
+ req->bytes_uncompressed = &bytes_uncompressed;
+
+ TRACE("Metadata block @ 0x%llx, %scompressed size %d, src size %d\n",
+ req->index, req->compressed ? "" : "un", bytes_read,
+ req->output->length);
+
+ ret = squashfs_bio_submit(req);
+ if (ret)
+ return ret;
+ if (error)
+ return error;
+ if (next_index)
+ *next_index += 2 + bytes_read;
+ return bytes_uncompressed;
+}
+
+static int read_data_block(struct squashfs_read_request *req, int length,
+ u64 *next_index, bool synchronous)
+{
+ int ret, error = 0, bytes_uncompressed = 0, bytes_read = 0;
+
+ req->compressed = SQUASHFS_COMPRESSED_BLOCK(length);
+ req->length = length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
+ req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS
+ : SQUASHFS_COPY;
+
+ req->synchronous = synchronous;
+ if (synchronous) {
+ req->res = &error;
+ req->bytes_read = &bytes_read;
+ req->bytes_uncompressed = &bytes_uncompressed;
+ }
+
+ TRACE("Data block @ 0x%llx, %scompressed size %d, src size %d\n",
+ req->index, req->compressed ? "" : "un", req->length,
+ req->output->length);
+
+ ret = squashfs_bio_submit(req);
+ if (ret)
+ return ret;
+ if (synchronous)
+ ret = error ? error : bytes_uncompressed;
+ if (next_index)
+ *next_index += length;
+ return ret;
+}
+
+/*
+ * Read and decompress a metadata block or datablock. Length is non-zero
+ * if a datablock is being read (the size is stored elsewhere in the
+ * filesystem), otherwise the length is obtained from the first two bytes of
+ * the metadata block. A bit in the length field indicates if the block
+ * is stored uncompressed in the filesystem (usually because compression
+ * generated a larger block - this does occasionally happen with compression
+ * algorithms).
+ */
+static int __squashfs_read_data(struct super_block *sb, u64 index, int length,
+ u64 *next_index, struct squashfs_page_actor *output, bool sync)
+{
+ struct squashfs_read_request *req;
+
+ req = kcalloc(1, sizeof(struct squashfs_read_request), GFP_KERNEL);
+ if (!req) {
+ if (!sync)
+ squashfs_page_actor_free(output, -ENOMEM);
+ return -ENOMEM;
+ }
+
+ req->sb = sb;
+ req->index = index;
+ req->output = output;
+
+ if (next_index)
+ *next_index = index;
+
+ if (length)
+ length = read_data_block(req, length, next_index, sync);
+ else
+ length = read_metadata_block(req, next_index);
+
+ if (length < 0) {
+ ERROR("squashfs_read_data failed to read block 0x%llx\n",
+ (unsigned long long)index);
+ return -EIO;
}
- kfree(bh);
return length;
+}
-block_release:
- for (; k < b; k++)
- put_bh(bh[k]);
+int squashfs_read_data(struct super_block *sb, u64 index, int length,
+ u64 *next_index, struct squashfs_page_actor *output)
+{
+ return __squashfs_read_data(sb, index, length, next_index, output,
+ true);
+}
+
+int squashfs_read_data_async(struct super_block *sb, u64 index, int length,
+ u64 *next_index, struct squashfs_page_actor *output)
+{
-read_failure:
- ERROR("squashfs_read_data failed to read block 0x%llx\n",
- (unsigned long long) index);
- kfree(bh);
- return -EIO;
+ return __squashfs_read_data(sb, index, length, next_index, output,
+ false);
}
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
index 1cb70a0b2168..6785d086ab38 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -209,17 +209,14 @@ void squashfs_cache_put(struct squashfs_cache_entry *entry)
*/
void squashfs_cache_delete(struct squashfs_cache *cache)
{
- int i, j;
+ int i;
if (cache == NULL)
return;
for (i = 0; i < cache->entries; i++) {
- if (cache->entry[i].data) {
- for (j = 0; j < cache->pages; j++)
- kfree(cache->entry[i].data[j]);
- kfree(cache->entry[i].data);
- }
+ if (cache->entry[i].page)
+ free_page_array(cache->entry[i].page, cache->pages);
kfree(cache->entry[i].actor);
}
@@ -236,7 +233,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
struct squashfs_cache *squashfs_cache_init(char *name, int entries,
int block_size)
{
- int i, j;
+ int i;
struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);
if (cache == NULL) {
@@ -268,22 +265,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,
init_waitqueue_head(&cache->entry[i].wait_queue);
entry->cache = cache;
entry->block = SQUASHFS_INVALID_BLK;
- entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
- if (entry->data == NULL) {
+ entry->page = alloc_page_array(cache->pages, GFP_KERNEL);
+ if (!entry->page) {
ERROR("Failed to allocate %s cache entry\n", name);
goto cleanup;
}
-
- for (j = 0; j < cache->pages; j++) {
- entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
- if (entry->data[j] == NULL) {
- ERROR("Failed to allocate %s buffer\n", name);
- goto cleanup;
- }
- }
-
- entry->actor = squashfs_page_actor_init(entry->data,
- cache->pages, 0);
+ entry->actor = squashfs_page_actor_init(entry->page,
+ cache->pages, 0, NULL);
if (entry->actor == NULL) {
ERROR("Failed to allocate %s cache entry\n", name);
goto cleanup;
@@ -314,18 +302,20 @@ int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
return min(length, entry->length - offset);
while (offset < entry->length) {
- void *buff = entry->data[offset / PAGE_CACHE_SIZE]
- + (offset % PAGE_CACHE_SIZE);
+ void *buff = kmap_atomic(entry->page[offset / PAGE_CACHE_SIZE])
+ + (offset % PAGE_CACHE_SIZE);
int bytes = min_t(int, entry->length - offset,
PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE));
if (bytes >= remaining) {
memcpy(buffer, buff, remaining);
+ kunmap_atomic(buff);
remaining = 0;
break;
}
memcpy(buffer, buff, bytes);
+ kunmap_atomic(buff);
buffer += bytes;
remaining -= bytes;
offset += bytes;
@@ -416,43 +406,38 @@ struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,
void *squashfs_read_table(struct super_block *sb, u64 block, int length)
{
int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
- int i, res;
- void *table, *buffer, **data;
+ struct page **page;
+ void *buff;
+ int res;
struct squashfs_page_actor *actor;
- table = buffer = kmalloc(length, GFP_KERNEL);
- if (table == NULL)
+ page = alloc_page_array(pages, GFP_KERNEL);
+ if (!page)
return ERR_PTR(-ENOMEM);
- data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
- if (data == NULL) {
- res = -ENOMEM;
- goto failed;
- }
-
- actor = squashfs_page_actor_init(data, pages, length);
+ actor = squashfs_page_actor_init(page, pages, length, NULL);
if (actor == NULL) {
res = -ENOMEM;
- goto failed2;
+ goto failed;
}
- for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
- data[i] = buffer;
-
res = squashfs_read_data(sb, block, length |
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
- kfree(data);
- kfree(actor);
-
if (res < 0)
- goto failed;
+ goto failed2;
- return table;
+ buff = kmalloc(length, GFP_KERNEL);
+ if (!buff)
+ goto failed2;
+ squashfs_actor_to_buf(actor, buff, length);
+ squashfs_page_actor_free(actor, 0);
+ free_page_array(page, pages);
+ return buff;
failed2:
- kfree(data);
+ squashfs_page_actor_free(actor, 0);
failed:
- kfree(table);
+ free_page_array(page, pages);
return ERR_PTR(res);
}
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
index e9034bf6e5ae..7de35bf297aa 100644
--- a/fs/squashfs/decompressor.c
+++ b/fs/squashfs/decompressor.c
@@ -24,7 +24,8 @@
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/buffer_head.h>
+#include <linux/highmem.h>
+#include <linux/fs.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -94,40 +95,44 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
static void *get_comp_opts(struct super_block *sb, unsigned short flags)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
- void *buffer = NULL, *comp_opts;
+ void *comp_opts, *buffer = NULL;
+ struct page *page;
struct squashfs_page_actor *actor = NULL;
int length = 0;
+ if (!SQUASHFS_COMP_OPTS(flags))
+ return squashfs_comp_opts(msblk, buffer, length);
+
/*
* Read decompressor specific options from file system if present
*/
- if (SQUASHFS_COMP_OPTS(flags)) {
- buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
- if (buffer == NULL) {
- comp_opts = ERR_PTR(-ENOMEM);
- goto out;
- }
-
- actor = squashfs_page_actor_init(&buffer, 1, 0);
- if (actor == NULL) {
- comp_opts = ERR_PTR(-ENOMEM);
- goto out;
- }
-
- length = squashfs_read_data(sb,
- sizeof(struct squashfs_super_block), 0, NULL, actor);
-
- if (length < 0) {
- comp_opts = ERR_PTR(length);
- goto out;
- }
+
+ page = alloc_page(GFP_KERNEL);
+ if (!page)
+ return ERR_PTR(-ENOMEM);
+
+ actor = squashfs_page_actor_init(&page, 1, 0, NULL);
+ if (actor == NULL) {
+ comp_opts = ERR_PTR(-ENOMEM);
+ goto actor_error;
+ }
+
+ length = squashfs_read_data(sb,
+ sizeof(struct squashfs_super_block), 0, NULL, actor);
+
+ if (length < 0) {
+ comp_opts = ERR_PTR(length);
+ goto read_error;
}
+ buffer = kmap_atomic(page);
comp_opts = squashfs_comp_opts(msblk, buffer, length);
+ kunmap_atomic(buffer);
-out:
- kfree(actor);
- kfree(buffer);
+read_error:
+ squashfs_page_actor_free(actor, 0);
+actor_error:
+ __free_page(page);
return comp_opts;
}
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
index e5c9689062ba..6f5ef8d7e55a 100644
--- a/fs/squashfs/file.c
+++ b/fs/squashfs/file.c
@@ -47,12 +47,16 @@
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mutex.h>
+#include <linux/mm_inline.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
+// Backported from 4.5
+#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))
+
/*
* Locate cache slot in range [offset, index] for specified inode. If
* there's more than one return the slot closest to index.
@@ -438,6 +442,21 @@ static int squashfs_readpage_fragment(struct page *page)
return res;
}
+static int squashfs_readpages_fragment(struct page *page,
+ struct list_head *readahead_pages, struct address_space *mapping)
+{
+ if (!page) {
+ page = lru_to_page(readahead_pages);
+ list_del(&page->lru);
+ if (add_to_page_cache_lru(page, mapping, page->index,
+ mapping_gfp_constraint(mapping, GFP_KERNEL))) {
+ put_page(page);
+ return 0;
+ }
+ }
+ return squashfs_readpage_fragment(page);
+}
+
static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
{
struct inode *inode = page->mapping->host;
@@ -450,54 +469,105 @@ static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
return 0;
}
-static int squashfs_readpage(struct file *file, struct page *page)
+static int squashfs_readpages_sparse(struct page *page,
+ struct list_head *readahead_pages, int index, int file_end,
+ struct address_space *mapping)
{
- struct inode *inode = page->mapping->host;
+ if (!page) {
+ page = lru_to_page(readahead_pages);
+ list_del(&page->lru);
+ if (add_to_page_cache_lru(page, mapping, page->index,
+ mapping_gfp_constraint(mapping, GFP_KERNEL))) {
+ put_page(page);
+ return 0;
+ }
+ }
+ return squashfs_readpage_sparse(page, index, file_end);
+}
+
+static int __squashfs_readpages(struct file *file, struct page *page,
+ struct list_head *readahead_pages, unsigned int nr_pages,
+ struct address_space *mapping)
+{
+ struct inode *inode = mapping->host;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
- int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
int file_end = i_size_read(inode) >> msblk->block_log;
int res;
- void *pageaddr;
- TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
- page->index, squashfs_i(inode)->start);
+ do {
+ struct page *cur_page = page ? page
+ : lru_to_page(readahead_pages);
+ int page_index = cur_page->index;
+ int index = page_index >> (msblk->block_log - PAGE_CACHE_SHIFT);
+
+ if (page_index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT))
+ return 1;
+
+ if (index < file_end || squashfs_i(inode)->fragment_block ==
+ SQUASHFS_INVALID_BLK) {
+ u64 block = 0;
+ int bsize = read_blocklist(inode, index, &block);
+
+ if (bsize < 0)
+ return -1;
+
+ if (bsize == 0) {
+ res = squashfs_readpages_sparse(page,
+ readahead_pages, index, file_end,
+ mapping);
+ } else {
+ res = squashfs_readpages_block(page,
+ readahead_pages, &nr_pages, mapping,
+ page_index, block, bsize);
+ }
+ } else {
+ res = squashfs_readpages_fragment(page,
+ readahead_pages, mapping);
+ }
+ if (res)
+ return 0;
+ page = NULL;
+ } while (readahead_pages && !list_empty(readahead_pages));
- if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
- PAGE_CACHE_SHIFT))
- goto out;
+ return 0;
+}
- if (index < file_end || squashfs_i(inode)->fragment_block ==
- SQUASHFS_INVALID_BLK) {
- u64 block = 0;
- int bsize = read_blocklist(inode, index, &block);
- if (bsize < 0)
- goto error_out;
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+ int ret;
- if (bsize == 0)
- res = squashfs_readpage_sparse(page, index, file_end);
+ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+ page->index, squashfs_i(page->mapping->host)->start);
+
+ get_page(page);
+
+ ret = __squashfs_readpages(file, page, NULL, 1, page->mapping);
+ if (ret) {
+ flush_dcache_page(page);
+ if (ret < 0)
+ SetPageError(page);
else
- res = squashfs_readpage_block(page, block, bsize);
- } else
- res = squashfs_readpage_fragment(page);
-
- if (!res)
- return 0;
-
-error_out:
- SetPageError(page);
-out:
- pageaddr = kmap_atomic(page);
- memset(pageaddr, 0, PAGE_CACHE_SIZE);
- kunmap_atomic(pageaddr);
- flush_dcache_page(page);
- if (!PageError(page))
- SetPageUptodate(page);
- unlock_page(page);
+ SetPageUptodate(page);
+ zero_user_segment(page, 0, PAGE_CACHE_SIZE);
+ unlock_page(page);
+ put_page(page);
+ }
return 0;
}
+static int squashfs_readpages(struct file *file, struct address_space *mapping,
+ struct list_head *pages, unsigned int nr_pages)
+{
+ TRACE("Entered squashfs_readpages, %u pages, first page index %lx\n",
+ nr_pages, lru_to_page(pages)->index);
+ __squashfs_readpages(file, NULL, pages, nr_pages, mapping);
+ return 0;
+}
+
const struct address_space_operations squashfs_aops = {
- .readpage = squashfs_readpage
+ .readpage = squashfs_readpage,
+ .readpages = squashfs_readpages,
};
diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c
deleted file mode 100644
index f2310d2a2019..000000000000
--- a/fs/squashfs/file_cache.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#include <linux/fs.h>
-#include <linux/vfs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/pagemap.h>
-#include <linux/mutex.h>
-
-#include "squashfs_fs.h"
-#include "squashfs_fs_sb.h"
-#include "squashfs_fs_i.h"
-#include "squashfs.h"
-
-/* Read separately compressed datablock and memcopy into page cache */
-int squashfs_readpage_block(struct page *page, u64 block, int bsize)
-{
- struct inode *i = page->mapping->host;
- struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
- block, bsize);
- int res = buffer->error;
-
- if (res)
- ERROR("Unable to read page, block %llx, size %x\n", block,
- bsize);
- else
- squashfs_copy_cache(page, buffer, buffer->length, 0);
-
- squashfs_cache_put(buffer);
- return res;
-}
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
index 43e7a7eddac0..c97af4c6ccd0 100644
--- a/fs/squashfs/file_direct.c
+++ b/fs/squashfs/file_direct.c
@@ -13,6 +13,7 @@
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mutex.h>
+#include <linux/mm_inline.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -20,157 +21,139 @@
#include "squashfs.h"
#include "page_actor.h"
-static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
- int pages, struct page **page);
-
-/* Read separately compressed datablock directly into page cache */
-int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
+// Backported from 4.5
+#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))
+static void release_actor_pages(struct page **page, int pages, int error)
{
- struct inode *inode = target_page->mapping->host;
- struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ int i;
- int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT;
- int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
- int start_index = target_page->index & ~mask;
- int end_index = start_index | mask;
- int i, n, pages, missing_pages, bytes, res = -ENOMEM;
+ for (i = 0; i < pages; i++) {
+ if (!page[i])
+ continue;
+ flush_dcache_page(page[i]);
+ if (!error)
+ SetPageUptodate(page[i]);
+ else {
+ SetPageError(page[i]);
+ zero_user_segment(page[i], 0, PAGE_CACHE_SIZE);
+ }
+ unlock_page(page[i]);
+ put_page(page[i]);
+ }
+ kfree(page);
+}
+
+/*
+ * Create a "page actor" which will kmap and kunmap the
+ * page cache pages appropriately within the decompressor
+ */
+static struct squashfs_page_actor *actor_from_page_cache(
+ unsigned int actor_pages, struct page *target_page,
+ struct list_head *rpages, unsigned int *nr_pages, int start_index,
+ struct address_space *mapping)
+{
struct page **page;
struct squashfs_page_actor *actor;
- void *pageaddr;
-
- if (end_index > file_end)
- end_index = file_end;
-
- pages = end_index - start_index + 1;
-
- page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL);
- if (page == NULL)
- return res;
-
- /*
- * Create a "page actor" which will kmap and kunmap the
- * page cache pages appropriately within the decompressor
- */
- actor = squashfs_page_actor_init_special(page, pages, 0);
- if (actor == NULL)
- goto out;
-
- /* Try to grab all the pages covered by the Squashfs block */
- for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
- page[i] = (n == target_page->index) ? target_page :
- grab_cache_page_nowait(target_page->mapping, n);
+ int i, n;
+ gfp_t gfp = mapping_gfp_constraint(mapping, GFP_KERNEL);
+
+ page = kmalloc_array(actor_pages, sizeof(void *), GFP_KERNEL);
+ if (!page)
+ return NULL;
+
+ for (i = 0, n = start_index; i < actor_pages; i++, n++) {
+ if (target_page == NULL && rpages && !list_empty(rpages)) {
+ struct page *cur_page = lru_to_page(rpages);
+
+ if (cur_page->index < start_index + actor_pages) {
+ list_del(&cur_page->lru);
+ --(*nr_pages);
+ if (add_to_page_cache_lru(cur_page, mapping,
+ cur_page->index, gfp))
+ put_page(cur_page);
+ else
+ target_page = cur_page;
+ } else
+ rpages = NULL;
+ }
- if (page[i] == NULL) {
- missing_pages++;
- continue;
+ if (target_page && target_page->index == n) {
+ page[i] = target_page;
+ target_page = NULL;
+ } else {
+ page[i] = grab_cache_page_nowait(mapping, n);
+ if (page[i] == NULL)
+ continue;
}
if (PageUptodate(page[i])) {
unlock_page(page[i]);
- page_cache_release(page[i]);
+ put_page(page[i]);
page[i] = NULL;
- missing_pages++;
}
}
- if (missing_pages) {
- /*
- * Couldn't get one or more pages, this page has either
- * been VM reclaimed, but others are still in the page cache
- * and uptodate, or we're racing with another thread in
- * squashfs_readpage also trying to grab them. Fall back to
- * using an intermediate buffer.
- */
- res = squashfs_read_cache(target_page, block, bsize, pages,
- page);
- if (res < 0)
- goto mark_errored;
-
- goto out;
+ actor = squashfs_page_actor_init(page, actor_pages, 0,
+ release_actor_pages);
+ if (!actor) {
+ release_actor_pages(page, actor_pages, -ENOMEM);
+ kfree(page);
+ return NULL;
}
-
- /* Decompress directly into the page cache buffers */
- res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
- if (res < 0)
- goto mark_errored;
-
- /* Last page may have trailing bytes not filled */
- bytes = res % PAGE_CACHE_SIZE;
- if (bytes) {
- pageaddr = kmap_atomic(page[pages - 1]);
- memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
- kunmap_atomic(pageaddr);
- }
-
- /* Mark pages as uptodate, unlock and release */
- for (i = 0; i < pages; i++) {
- flush_dcache_page(page[i]);
- SetPageUptodate(page[i]);
- unlock_page(page[i]);
- if (page[i] != target_page)
- page_cache_release(page[i]);
- }
-
- kfree(actor);
- kfree(page);
-
- return 0;
-
-mark_errored:
- /* Decompression failed, mark pages as errored. Target_page is
- * dealt with by the caller
- */
- for (i = 0; i < pages; i++) {
- if (page[i] == NULL || page[i] == target_page)
- continue;
- flush_dcache_page(page[i]);
- SetPageError(page[i]);
- unlock_page(page[i]);
- page_cache_release(page[i]);
- }
-
-out:
- kfree(actor);
- kfree(page);
- return res;
+ return actor;
}
+int squashfs_readpages_block(struct page *target_page,
+ struct list_head *readahead_pages,
+ unsigned int *nr_pages,
+ struct address_space *mapping,
+ int page_index, u64 block, int bsize)
-static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
- int pages, struct page **page)
{
- struct inode *i = target_page->mapping->host;
- struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
- block, bsize);
- int bytes = buffer->length, res = buffer->error, n, offset = 0;
- void *pageaddr;
-
- if (res) {
- ERROR("Unable to read page, block %llx, size %x\n", block,
- bsize);
- goto out;
- }
-
- for (n = 0; n < pages && bytes > 0; n++,
- bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
- int avail = min_t(int, bytes, PAGE_CACHE_SIZE);
-
- if (page[n] == NULL)
- continue;
+ struct squashfs_page_actor *actor;
+ struct inode *inode = mapping->host;
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ int start_index, end_index, file_end, actor_pages, res;
+ int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
- pageaddr = kmap_atomic(page[n]);
- squashfs_copy_data(pageaddr, buffer, offset, avail);
- memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
- kunmap_atomic(pageaddr);
- flush_dcache_page(page[n]);
- SetPageUptodate(page[n]);
- unlock_page(page[n]);
- if (page[n] != target_page)
- page_cache_release(page[n]);
+ /*
+ * If readpage() is called on an uncompressed datablock, we can just
+ * read the pages instead of fetching the whole block.
+ * This greatly improves the performance when a process keep doing
+ * random reads because we only fetch the necessary data.
+ * The readahead algorithm will take care of doing speculative reads
+ * if necessary.
+ * We can't read more than 1 block even if readahead provides use more
+ * pages because we don't know yet if the next block is compressed or
+ * not.
+ */
+ if (bsize && !SQUASHFS_COMPRESSED_BLOCK(bsize)) {
+ u64 block_end = block + msblk->block_size;
+
+ block += (page_index & mask) * PAGE_CACHE_SIZE;
+ actor_pages = (block_end - block) / PAGE_CACHE_SIZE;
+ if (*nr_pages < actor_pages)
+ actor_pages = *nr_pages;
+ start_index = page_index;
+ bsize = min_t(int, bsize, (PAGE_CACHE_SIZE * actor_pages)
+ | SQUASHFS_COMPRESSED_BIT_BLOCK);
+ } else {
+ file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT;
+ start_index = page_index & ~mask;
+ end_index = start_index | mask;
+ if (end_index > file_end)
+ end_index = file_end;
+ actor_pages = end_index - start_index + 1;
}
-out:
- squashfs_cache_put(buffer);
- return res;
+ actor = actor_from_page_cache(actor_pages, target_page,
+ readahead_pages, nr_pages, start_index,
+ mapping);
+ if (!actor)
+ return -ENOMEM;
+
+ res = squashfs_read_data_async(inode->i_sb, block, bsize, NULL,
+ actor);
+ return res < 0 ? res : 0;
}
diff --git a/fs/squashfs/lz4_wrapper.c b/fs/squashfs/lz4_wrapper.c
index c31e2bc9c081..df4fa3c7ddd0 100644
--- a/fs/squashfs/lz4_wrapper.c
+++ b/fs/squashfs/lz4_wrapper.c
@@ -94,39 +94,17 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
struct buffer_head **bh, int b, int offset, int length,
struct squashfs_page_actor *output)
{
- struct squashfs_lz4 *stream = strm;
- void *buff = stream->input, *data;
- int avail, i, bytes = length, res;
+ int res;
size_t dest_len = output->length;
+ struct squashfs_lz4 *stream = strm;
- for (i = 0; i < b; i++) {
- avail = min(bytes, msblk->devblksize - offset);
- memcpy(buff, bh[i]->b_data + offset, avail);
- buff += avail;
- bytes -= avail;
- offset = 0;
- put_bh(bh[i]);
- }
-
+ squashfs_bh_to_buf(bh, b, stream->input, offset, length,
+ msblk->devblksize);
res = lz4_decompress_unknownoutputsize(stream->input, length,
stream->output, &dest_len);
if (res)
return -EIO;
-
- bytes = dest_len;
- data = squashfs_first_page(output);
- buff = stream->output;
- while (data) {
- if (bytes <= PAGE_CACHE_SIZE) {
- memcpy(data, buff, bytes);
- break;
- }
- memcpy(data, buff, PAGE_CACHE_SIZE);
- buff += PAGE_CACHE_SIZE;
- bytes -= PAGE_CACHE_SIZE;
- data = squashfs_next_page(output);
- }
- squashfs_finish_page(output);
+ squashfs_buf_to_actor(stream->output, output, dest_len);
return dest_len;
}
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
index 244b9fbfff7b..2c844d53a59e 100644
--- a/fs/squashfs/lzo_wrapper.c
+++ b/fs/squashfs/lzo_wrapper.c
@@ -79,45 +79,19 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
struct buffer_head **bh, int b, int offset, int length,
struct squashfs_page_actor *output)
{
- struct squashfs_lzo *stream = strm;
- void *buff = stream->input, *data;
- int avail, i, bytes = length, res;
+ int res;
size_t out_len = output->length;
+ struct squashfs_lzo *stream = strm;
- for (i = 0; i < b; i++) {
- avail = min(bytes, msblk->devblksize - offset);
- memcpy(buff, bh[i]->b_data + offset, avail);
- buff += avail;
- bytes -= avail;
- offset = 0;
- put_bh(bh[i]);
- }
-
+ squashfs_bh_to_buf(bh, b, stream->input, offset, length,
+ msblk->devblksize);
res = lzo1x_decompress_safe(stream->input, (size_t)length,
stream->output, &out_len);
if (res != LZO_E_OK)
- goto failed;
+ return -EIO;
+ squashfs_buf_to_actor(stream->output, output, out_len);
- res = bytes = (int)out_len;
- data = squashfs_first_page(output);
- buff = stream->output;
- while (data) {
- if (bytes <= PAGE_CACHE_SIZE) {
- memcpy(data, buff, bytes);
- break;
- } else {
- memcpy(data, buff, PAGE_CACHE_SIZE);
- buff += PAGE_CACHE_SIZE;
- bytes -= PAGE_CACHE_SIZE;
- data = squashfs_next_page(output);
- }
- }
- squashfs_finish_page(output);
-
- return res;
-
-failed:
- return -EIO;
+ return out_len;
}
const struct squashfs_decompressor squashfs_lzo_comp_ops = {
diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c
index 5a1c11f56441..53863508e400 100644
--- a/fs/squashfs/page_actor.c
+++ b/fs/squashfs/page_actor.c
@@ -9,39 +9,11 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
+#include <linux/buffer_head.h>
#include "page_actor.h"
-/*
- * This file contains implementations of page_actor for decompressing into
- * an intermediate buffer, and for decompressing directly into the
- * page cache.
- *
- * Calling code should avoid sleeping between calls to squashfs_first_page()
- * and squashfs_finish_page().
- */
-
-/* Implementation of page_actor for decompressing into intermediate buffer */
-static void *cache_first_page(struct squashfs_page_actor *actor)
-{
- actor->next_page = 1;
- return actor->buffer[0];
-}
-
-static void *cache_next_page(struct squashfs_page_actor *actor)
-{
- if (actor->next_page == actor->pages)
- return NULL;
-
- return actor->buffer[actor->next_page++];
-}
-
-static void cache_finish_page(struct squashfs_page_actor *actor)
-{
- /* empty */
-}
-
-struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
- int pages, int length)
+struct squashfs_page_actor *squashfs_page_actor_init(struct page **page,
+ int pages, int length, void (*release_pages)(struct page **, int, int))
{
struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
@@ -49,52 +21,133 @@ struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
return NULL;
actor->length = length ? : pages * PAGE_CACHE_SIZE;
- actor->buffer = buffer;
+ actor->page = page;
actor->pages = pages;
actor->next_page = 0;
- actor->squashfs_first_page = cache_first_page;
- actor->squashfs_next_page = cache_next_page;
- actor->squashfs_finish_page = cache_finish_page;
+ actor->pageaddr = NULL;
+ actor->release_pages = release_pages;
return actor;
}
-/* Implementation of page_actor for decompressing directly into page cache. */
-static void *direct_first_page(struct squashfs_page_actor *actor)
+void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error)
+{
+ if (!actor)
+ return;
+
+ if (actor->release_pages)
+ actor->release_pages(actor->page, actor->pages, error);
+ kfree(actor);
+}
+
+void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf,
+ int length)
{
- actor->next_page = 1;
- return actor->pageaddr = kmap_atomic(actor->page[0]);
+ void *pageaddr;
+ int pos = 0, avail, i;
+
+ for (i = 0; i < actor->pages && pos < length; ++i) {
+ avail = min_t(int, length - pos, PAGE_CACHE_SIZE);
+ if (actor->page[i]) {
+ pageaddr = kmap_atomic(actor->page[i]);
+ memcpy(buf + pos, pageaddr, avail);
+ kunmap_atomic(pageaddr);
+ }
+ pos += avail;
+ }
}
-static void *direct_next_page(struct squashfs_page_actor *actor)
+void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor,
+ int length)
{
- if (actor->pageaddr)
- kunmap_atomic(actor->pageaddr);
+ void *pageaddr;
+ int pos = 0, avail, i;
+
+ for (i = 0; i < actor->pages && pos < length; ++i) {
+ avail = min_t(int, length - pos, PAGE_CACHE_SIZE);
+ if (actor->page[i]) {
+ pageaddr = kmap_atomic(actor->page[i]);
+ memcpy(pageaddr, buf + pos, avail);
+ kunmap_atomic(pageaddr);
+ }
+ pos += avail;
+ }
+}
- return actor->pageaddr = actor->next_page == actor->pages ? NULL :
- kmap_atomic(actor->page[actor->next_page++]);
+void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers,
+ struct squashfs_page_actor *actor, int offset, int length, int blksz)
+{
+ void *kaddr = NULL;
+ int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i;
+
+ while (bytes < length) {
+ if (actor->page[p]) {
+ kaddr = kmap_atomic(actor->page[p]);
+ while (pgoff < PAGE_CACHE_SIZE && bytes < length) {
+ avail = min_t(int, blksz - offset,
+ PAGE_CACHE_SIZE - pgoff);
+ memcpy(kaddr + pgoff, bh[b]->b_data + offset,
+ avail);
+ pgoff += avail;
+ bytes += avail;
+ offset = (offset + avail) % blksz;
+ if (!offset) {
+ put_bh(bh[b]);
+ ++b;
+ }
+ }
+ kunmap_atomic(kaddr);
+ pgoff = 0;
+ } else {
+ for (i = 0; i < PAGE_CACHE_SIZE / blksz; ++i) {
+ if (bh[b])
+ put_bh(bh[b]);
+ ++b;
+ }
+ bytes += PAGE_CACHE_SIZE;
+ }
+ ++p;
+ }
}
-static void direct_finish_page(struct squashfs_page_actor *actor)
+void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf,
+ int offset, int length, int blksz)
{
- if (actor->pageaddr)
- kunmap_atomic(actor->pageaddr);
+ int i, avail, bytes = 0;
+
+ for (i = 0; i < nr_buffers && bytes < length; ++i) {
+ avail = min_t(int, length - bytes, blksz - offset);
+ if (bh[i]) {
+ memcpy(buf + bytes, bh[i]->b_data + offset, avail);
+ put_bh(bh[i]);
+ }
+ bytes += avail;
+ offset = 0;
+ }
}
-struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
- int pages, int length)
+void free_page_array(struct page **page, int nr_pages)
{
- struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+ int i;
- if (actor == NULL)
- return NULL;
+ for (i = 0; i < nr_pages; ++i)
+ __free_page(page[i]);
+ kfree(page);
+}
- actor->length = length ? : pages * PAGE_CACHE_SIZE;
- actor->page = page;
- actor->pages = pages;
- actor->next_page = 0;
- actor->pageaddr = NULL;
- actor->squashfs_first_page = direct_first_page;
- actor->squashfs_next_page = direct_next_page;
- actor->squashfs_finish_page = direct_finish_page;
- return actor;
+struct page **alloc_page_array(int nr_pages, int gfp_mask)
+{
+ int i;
+ struct page **page;
+
+ page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask);
+ if (!page)
+ return NULL;
+ for (i = 0; i < nr_pages; ++i) {
+ page[i] = alloc_page(gfp_mask);
+ if (!page[i]) {
+ free_page_array(page, i);
+ return NULL;
+ }
+ }
+ return page;
}
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
index 26dd82008b82..aa1ed790b5a3 100644
--- a/fs/squashfs/page_actor.h
+++ b/fs/squashfs/page_actor.h
@@ -5,77 +5,61 @@
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
+ * the COPYING file in the top-level squashfsory.
*/
-#ifndef CONFIG_SQUASHFS_FILE_DIRECT
struct squashfs_page_actor {
- void **page;
+ struct page **page;
+ void *pageaddr;
int pages;
int length;
int next_page;
+ void (*release_pages)(struct page **, int, int);
};
-static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
- int pages, int length)
-{
- struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
-
- if (actor == NULL)
- return NULL;
+extern struct squashfs_page_actor *squashfs_page_actor_init(struct page **,
+ int, int, void (*)(struct page **, int, int));
+extern void squashfs_page_actor_free(struct squashfs_page_actor *, int);
- actor->length = length ? : pages * PAGE_CACHE_SIZE;
- actor->page = page;
- actor->pages = pages;
- actor->next_page = 0;
- return actor;
-}
+extern void squashfs_actor_to_buf(struct squashfs_page_actor *, void *, int);
+extern void squashfs_buf_to_actor(void *, struct squashfs_page_actor *, int);
+extern void squashfs_bh_to_actor(struct buffer_head **, int,
+ struct squashfs_page_actor *, int, int, int);
+extern void squashfs_bh_to_buf(struct buffer_head **, int, void *, int, int,
+ int);
+/*
+ * Calling code should avoid sleeping between calls to squashfs_first_page()
+ * and squashfs_finish_page().
+ */
static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
{
actor->next_page = 1;
- return actor->page[0];
+ return actor->pageaddr = actor->page[0] ? kmap_atomic(actor->page[0])
+ : NULL;
}
static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
{
- return actor->next_page == actor->pages ? NULL :
- actor->page[actor->next_page++];
-}
+ if (!IS_ERR_OR_NULL(actor->pageaddr))
+ kunmap_atomic(actor->pageaddr);
-static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
-{
- /* empty */
-}
-#else
-struct squashfs_page_actor {
- union {
- void **buffer;
- struct page **page;
- };
- void *pageaddr;
- void *(*squashfs_first_page)(struct squashfs_page_actor *);
- void *(*squashfs_next_page)(struct squashfs_page_actor *);
- void (*squashfs_finish_page)(struct squashfs_page_actor *);
- int pages;
- int length;
- int next_page;
-};
+ if (actor->next_page == actor->pages)
+ return actor->pageaddr = ERR_PTR(-ENODATA);
-extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
-extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
- **, int, int);
-static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
-{
- return actor->squashfs_first_page(actor);
-}
-static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
-{
- return actor->squashfs_next_page(actor);
+ actor->pageaddr = actor->page[actor->next_page] ?
+ kmap_atomic(actor->page[actor->next_page]) : NULL;
+ ++actor->next_page;
+ return actor->pageaddr;
}
+
static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
{
- actor->squashfs_finish_page(actor);
+ if (!IS_ERR_OR_NULL(actor->pageaddr))
+ kunmap_atomic(actor->pageaddr);
}
-#endif
+
+extern struct page **alloc_page_array(int, int);
+extern void free_page_array(struct page **, int);
+
#endif
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 887d6d270080..6093579c6c5d 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -28,8 +28,14 @@
#define WARNING(s, args...) pr_warn("SQUASHFS: "s, ## args)
/* block.c */
+extern int squashfs_init_read_wq(void);
+extern void squashfs_destroy_read_wq(void);
extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
struct squashfs_page_actor *);
+extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
+ struct squashfs_page_actor *);
+extern int squashfs_read_data_async(struct super_block *, u64, int, u64 *,
+ struct squashfs_page_actor *);
/* cache.c */
extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
@@ -70,8 +76,9 @@ extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
int);
-/* file_xxx.c */
-extern int squashfs_readpage_block(struct page *, u64, int);
+/* file_direct.c */
+extern int squashfs_readpages_block(struct page *, struct list_head *,
+ unsigned int *, struct address_space *, int, u64, int);
/* id.c */
extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index 1da565cb50c3..8a6995de0277 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -49,7 +49,7 @@ struct squashfs_cache_entry {
int num_waiters;
wait_queue_head_t wait_queue;
struct squashfs_cache *cache;
- void **data;
+ struct page **page;
struct squashfs_page_actor *actor;
};
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 5056babe00df..61cd0b39ed0e 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -444,9 +444,15 @@ static int __init init_squashfs_fs(void)
if (err)
return err;
+ if (!squashfs_init_read_wq()) {
+ destroy_inodecache();
+ return -ENOMEM;
+ }
+
err = register_filesystem(&squashfs_fs_type);
if (err) {
destroy_inodecache();
+ squashfs_destroy_read_wq();
return err;
}
@@ -460,6 +466,7 @@ static void __exit exit_squashfs_fs(void)
{
unregister_filesystem(&squashfs_fs_type);
destroy_inodecache();
+ squashfs_destroy_read_wq();
}
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c
index c609624e4b8a..14cd373e1897 100644
--- a/fs/squashfs/xz_wrapper.c
+++ b/fs/squashfs/xz_wrapper.c
@@ -55,7 +55,7 @@ static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk,
struct comp_opts *opts;
int err = 0, n;
- opts = kmalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kmalloc(sizeof(*opts), GFP_ATOMIC);
if (opts == NULL) {
err = -ENOMEM;
goto out2;
@@ -136,6 +136,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
enum xz_ret xz_err;
int avail, total = 0, k = 0;
struct squashfs_xz *stream = strm;
+ void *buf = NULL;
xz_dec_reset(stream->state);
stream->buf.in_pos = 0;
@@ -156,12 +157,20 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
if (stream->buf.out_pos == stream->buf.out_size) {
stream->buf.out = squashfs_next_page(output);
- if (stream->buf.out != NULL) {
+ if (!IS_ERR(stream->buf.out)) {
stream->buf.out_pos = 0;
total += PAGE_CACHE_SIZE;
}
}
+ if (!stream->buf.out) {
+ if (!buf) {
+ buf = kmalloc(PAGE_CACHE_SIZE, GFP_ATOMIC);
+ if (!buf)
+ goto out;
+ }
+ stream->buf.out = buf;
+ }
xz_err = xz_dec_run(stream->state, &stream->buf);
if (stream->buf.in_pos == stream->buf.in_size && k < b)
@@ -173,11 +182,13 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
if (xz_err != XZ_STREAM_END || k < b)
goto out;
+ kfree(buf);
return total + stream->buf.out_pos;
out:
for (; k < b; k++)
put_bh(bh[k]);
+ kfree(buf);
return -EIO;
}
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
index 8727caba6882..09c892b5308e 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -66,6 +66,7 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
struct buffer_head **bh, int b, int offset, int length,
struct squashfs_page_actor *output)
{
+ void *buf = NULL;
int zlib_err, zlib_init = 0, k = 0;
z_stream *stream = strm;
@@ -84,10 +85,19 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
if (stream->avail_out == 0) {
stream->next_out = squashfs_next_page(output);
- if (stream->next_out != NULL)
+ if (!IS_ERR(stream->next_out))
stream->avail_out = PAGE_CACHE_SIZE;
}
+ if (!stream->next_out) {
+ if (!buf) {
+ buf = kmalloc(PAGE_CACHE_SIZE, GFP_ATOMIC);
+ if (!buf)
+ goto out;
+ }
+ stream->next_out = buf;
+ }
+
if (!zlib_init) {
zlib_err = zlib_inflateInit(stream);
if (zlib_err != Z_OK) {
@@ -115,11 +125,13 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
if (k < b)
goto out;
+ kfree(buf);
return stream->total_out;
out:
for (; k < b; k++)
put_bh(bh[k]);
+ kfree(buf);
return -EIO;
}
diff --git a/fs/sync.c b/fs/sync.c
index dd5d1711c7ac..452179e31c39 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -218,6 +218,7 @@ static int do_fsync(unsigned int fd, int datasync)
if (f.file) {
ret = vfs_fsync(f.file, datasync);
fdput(f);
+ inc_syscfs(current);
}
return ret;
}
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 1aabfda669b0..7183b7ea065b 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -299,6 +299,14 @@ xfs_dinode_verify(
if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC))
return false;
+ /* don't allow invalid i_size */
+ if (be64_to_cpu(dip->di_size) & (1ULL << 63))
+ return false;
+
+ /* No zero-length symlinks. */
+ if (S_ISLNK(be16_to_cpu(dip->di_mode)) && dip->di_size == 0)
+ return false;
+
/* only version 3 or greater inodes are extensively verified here */
if (dip->di_version < 3)
return true;
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index c5101a3295d8..62ba66e1c598 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -289,8 +289,10 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (type == ACL_TYPE_ACCESS) {
umode_t mode;
-
+ struct posix_acl *old_acl = acl;
error = posix_acl_update_mode(inode, &mode, &acl);
+ if (!acl)
+ posix_acl_release(old_acl);
if (error)
return error;
error = xfs_set_mode(inode, mode);
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index dbae6490a79a..832764ee035a 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1713,6 +1713,7 @@ xfs_swap_extents(
xfs_trans_t *tp;
xfs_bstat_t *sbp = &sxp->sx_stat;
xfs_ifork_t *tempifp, *ifp, *tifp;
+ xfs_extnum_t nextents;
int src_log_flags, target_log_flags;
int error = 0;
int aforkblks = 0;
@@ -1899,7 +1900,8 @@ xfs_swap_extents(
* pointer. Otherwise it's already NULL or
* pointing to the extent.
*/
- if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) {
+ nextents = ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ if (nextents <= XFS_INLINE_EXTS) {
ifp->if_u1.if_extents =
ifp->if_u2.if_inline_ext;
}
@@ -1918,7 +1920,8 @@ xfs_swap_extents(
* pointer. Otherwise it's already NULL or
* pointing to the extent.
*/
- if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) {
+ nextents = tip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ if (nextents <= XFS_INLINE_EXTS) {
tifp->if_u1.if_extents =
tifp->if_u2.if_inline_ext;
}
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index eb1b8c8acfcb..8146b0cf20ce 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -375,6 +375,7 @@ retry:
out_free_pages:
for (i = 0; i < bp->b_page_count; i++)
__free_page(bp->b_pages[i]);
+ bp->b_flags &= ~_XBF_PAGES;
return error;
}
diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h
index 4b4b056a6eb0..5148150cc80b 100644
--- a/include/asm-generic/memory_model.h
+++ b/include/asm-generic/memory_model.h
@@ -1,6 +1,8 @@
#ifndef __ASM_MEMORY_MODEL_H
#define __ASM_MEMORY_MODEL_H
+#include <linux/pfn.h>
+
#ifndef __ASSEMBLY__
#if defined(CONFIG_FLATMEM)
@@ -72,7 +74,7 @@
/*
* Convert a physical address to a Page Frame Number and back
*/
-#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT))
+#define __phys_to_pfn(paddr) PHYS_PFN(paddr)
#define __pfn_to_phys(pfn) PFN_PHYS(pfn)
#define page_to_pfn __page_to_pfn
diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h
index c9fe145f7dd3..04661e1fb625 100644
--- a/include/crypto/algapi.h
+++ b/include/crypto/algapi.h
@@ -202,6 +202,9 @@ int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc,
struct blkcipher_walk *walk,
struct crypto_aead *tfm,
unsigned int blocksize);
+int blkcipher_ablkcipher_walk_virt(struct blkcipher_desc *desc,
+ struct blkcipher_walk *walk,
+ struct crypto_ablkcipher *tfm);
int ablkcipher_walk_done(struct ablkcipher_request *req,
struct ablkcipher_walk *walk, int err);
diff --git a/include/crypto/gf128mul.h b/include/crypto/gf128mul.h
index da2530e34b26..230760aef93b 100644
--- a/include/crypto/gf128mul.h
+++ b/include/crypto/gf128mul.h
@@ -43,7 +43,7 @@
---------------------------------------------------------------------------
Issue Date: 31/01/2006
- An implementation of field multiplication in Galois Field GF(128)
+ An implementation of field multiplication in Galois Field GF(2^128)
*/
#ifndef _CRYPTO_GF128MUL_H
@@ -65,7 +65,7 @@
* are left and the lsb's are right. char b[16] is an array and b[0] is
* the first octet.
*
- * 80000000 00000000 00000000 00000000 .... 00000000 00000000 00000000
+ * 10000000 00000000 00000000 00000000 .... 00000000 00000000 00000000
* b[0] b[1] b[2] b[3] b[13] b[14] b[15]
*
* Every bit is a coefficient of some power of X. We can store the bits
@@ -99,21 +99,21 @@
*
* bbe on a little endian machine u32 x[4]:
*
- * MS x[0] LS MS x[1] LS
+ * MS x[0] LS MS x[1] LS
* ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
* 103..96 111.104 119.112 127.120 71...64 79...72 87...80 95...88
*
- * MS x[2] LS MS x[3] LS
+ * MS x[2] LS MS x[3] LS
* ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
* 39...32 47...40 55...48 63...56 07...00 15...08 23...16 31...24
*
* ble on a little endian machine
*
- * MS x[0] LS MS x[1] LS
+ * MS x[0] LS MS x[1] LS
* ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
* 31...24 23...16 15...08 07...00 63...56 55...48 47...40 39...32
*
- * MS x[2] LS MS x[3] LS
+ * MS x[2] LS MS x[3] LS
* ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
* 95...88 87...80 79...72 71...64 127.120 199.112 111.104 103..96
*
@@ -127,7 +127,7 @@
* machines this will automatically aligned to wordsize and on a 64-bit
* machine also.
*/
-/* Multiply a GF128 field element by x. Field elements are held in arrays
+/* Multiply a GF128 field element by x. Field elements are held in arrays
of bytes in which field bits 8n..8n + 7 are held in byte[n], with lower
indexed bits placed in the more numerically significant bit positions
within bytes.
@@ -135,62 +135,65 @@
On little endian machines the bit indexes translate into the bit
positions within four 32-bit words in the following way
- MS x[0] LS MS x[1] LS
+ MS x[0] LS MS x[1] LS
ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
24...31 16...23 08...15 00...07 56...63 48...55 40...47 32...39
- MS x[2] LS MS x[3] LS
+ MS x[2] LS MS x[3] LS
ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
88...95 80...87 72...79 64...71 120.127 112.119 104.111 96..103
On big endian machines the bit indexes translate into the bit
positions within four 32-bit words in the following way
- MS x[0] LS MS x[1] LS
+ MS x[0] LS MS x[1] LS
ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
00...07 08...15 16...23 24...31 32...39 40...47 48...55 56...63
- MS x[2] LS MS x[3] LS
+ MS x[2] LS MS x[3] LS
ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
64...71 72...79 80...87 88...95 96..103 104.111 112.119 120.127
*/
-/* A slow generic version of gf_mul, implemented for lle and bbe
- * It multiplies a and b and puts the result in a */
+/* A slow generic version of gf_mul, implemented for lle, bbe, and ble.
+ * It multiplies a and b and puts the result in a
+ */
void gf128mul_lle(be128 *a, const be128 *b);
-
void gf128mul_bbe(be128 *a, const be128 *b);
+void gf128mul_ble(be128 *a, const be128 *b);
-/* multiply by x in ble format, needed by XTS */
+/* multiply by x in ble format, needed by XTS and HEH */
void gf128mul_x_ble(be128 *a, const be128 *b);
/* 4k table optimization */
-
struct gf128mul_4k {
be128 t[256];
};
struct gf128mul_4k *gf128mul_init_4k_lle(const be128 *g);
struct gf128mul_4k *gf128mul_init_4k_bbe(const be128 *g);
+struct gf128mul_4k *gf128mul_init_4k_ble(const be128 *g);
void gf128mul_4k_lle(be128 *a, struct gf128mul_4k *t);
void gf128mul_4k_bbe(be128 *a, struct gf128mul_4k *t);
+void gf128mul_4k_ble(be128 *a, struct gf128mul_4k *t);
static inline void gf128mul_free_4k(struct gf128mul_4k *t)
{
- kfree(t);
+ kzfree(t);
}
-/* 64k table optimization, implemented for lle and bbe */
+/* 64k table optimization, implemented for lle, ble, and bbe */
struct gf128mul_64k {
struct gf128mul_4k *t[16];
};
-/* first initialize with the constant factor with which you
- * want to multiply and then call gf128_64k_lle with the other
- * factor in the first argument, the table in the second and a
- * scratch register in the third. Afterwards *a = *r. */
+/* First initialize with the constant factor with which you
+ * want to multiply and then call gf128mul_64k_bbe with the other
+ * factor in the first argument, and the table in the second.
+ * Afterwards, the result is stored in *a.
+ */
struct gf128mul_64k *gf128mul_init_64k_lle(const be128 *g);
struct gf128mul_64k *gf128mul_init_64k_bbe(const be128 *g);
void gf128mul_free_64k(struct gf128mul_64k *t);
diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h
index 3b4af1d7c7e9..476d99d0edb7 100644
--- a/include/crypto/internal/hash.h
+++ b/include/crypto/internal/hash.h
@@ -102,6 +102,8 @@ int shash_register_instance(struct crypto_template *tmpl,
struct shash_instance *inst);
void shash_free_instance(struct crypto_instance *inst);
+int crypto_grab_shash(struct crypto_shash_spawn *spawn,
+ const char *name, u32 type, u32 mask);
int crypto_init_shash_spawn(struct crypto_shash_spawn *spawn,
struct shash_alg *alg,
struct crypto_instance *inst);
@@ -111,6 +113,12 @@ static inline void crypto_drop_shash(struct crypto_shash_spawn *spawn)
crypto_drop_spawn(&spawn->base);
}
+static inline struct shash_alg *crypto_spawn_shash_alg(
+ struct crypto_shash_spawn *spawn)
+{
+ return container_of(spawn->base.alg, struct shash_alg, base);
+}
+
struct shash_alg *shash_attr_alg(struct rtattr *rta, u32 type, u32 mask);
int shash_ahash_update(struct ahash_request *req, struct shash_desc *desc);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index aab1530661a5..6555fdbee88a 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -644,6 +644,20 @@ struct drm_encoder {
* @audio_latency: audio latency info from ELD, if found
* @null_edid_counter: track sinks that give us all zeros for the EDID
* @bad_edid_counter: track sinks that give us an EDID with invalid checksum
+ * @max_tmds_char: indicates the maximum TMDS Character Rate supported
+ * @scdc_present: when set the sink supports SCDC functionality
+ * @rr_capable: when set the sink is capable of initiating an SCDC read request
+ * @supports_scramble: when set the sink supports less than 340Mcsc scrambling
+ * @flags_3d: 3D view(s) supported by the sink, see drm_edid.h (DRM_EDID_3D_*)
+ * @pt_scan_info: PT scan info obtained from the VCDB of EDID
+ * @it_scan_info: IT scan info obtained from the VCDB of EDID
+ * @ce_scan_info: CE scan info obtained from the VCDB of EDID
+ * @hdr_eotf: Electro optical transfer function obtained from HDR block
+ * @hdr_metadata_type_one: Metadata type one obtained from HDR block
+ * @hdr_max_luminance: desired max luminance obtained from HDR block
+ * @hdr_avg_luminance: desired avg luminance obtained from HDR block
+ * @hdr_min_luminance: desired min luminance obtained from HDR block
+ * @hdr_supported: does the sink support HDR content
* @edid_corrupt: indicates whether the last read EDID was corrupt
* @debugfs_entry: debugfs directory for this connector
* @state: current atomic state for this connector
@@ -717,6 +731,21 @@ struct drm_connector {
int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */
unsigned bad_edid_counter;
+ /* EDID bits HDMI 2.0 */
+ int max_tmds_char; /* in Mcsc */
+ bool scdc_present;
+ bool rr_capable;
+ bool supports_scramble;
+ int flags_3d;
+ u8 pt_scan_info;
+ u8 it_scan_info;
+ u8 ce_scan_info;
+ u32 hdr_eotf;
+ bool hdr_metadata_type_one;
+ u32 hdr_max_luminance;
+ u32 hdr_avg_luminance;
+ u32 hdr_min_luminance;
+ bool hdr_supported;
/* Flag for raw EDID header corruption - used in Displayport
* compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6
*/
diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h
index 623b4e98e748..c0d4df6a606f 100644
--- a/include/drm/drm_displayid.h
+++ b/include/drm/drm_displayid.h
@@ -73,4 +73,21 @@ struct displayid_tiled_block {
u8 topology_id[8];
} __packed;
+struct displayid_detailed_timings_1 {
+ u8 pixel_clock[3];
+ u8 flags;
+ u8 hactive[2];
+ u8 hblank[2];
+ u8 hsync[2];
+ u8 hsw[2];
+ u8 vactive[2];
+ u8 vblank[2];
+ u8 vsync[2];
+ u8 vsw[2];
+} __packed;
+
+struct displayid_detailed_timing_block {
+ struct displayid_block base;
+ struct displayid_detailed_timings_1 timings[0];
+};
#endif
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 2af97691e878..4317ea41382f 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -266,6 +266,11 @@ struct detailed_timing {
#define DRM_ELD_CEA_SAD(mnl, sad) (20 + (mnl) + 3 * (sad))
+/* HDMI 2.0 */
+#define DRM_EDID_3D_INDEPENDENT_VIEW (1 << 2)
+#define DRM_EDID_3D_DUAL_VIEW (1 << 1)
+#define DRM_EDID_3D_OSD_DISPARITY (1 << 0)
+
struct edid {
u8 header[8];
/* Vendor & product info */
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 702b6c53c12f..2043af2141c1 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -161,8 +161,8 @@ 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 path *, struct path *);
struct dentry *(*d_real)(struct dentry *, struct inode *);
+ void (*d_canonical_path)(const struct path *, struct path *);
} ____cacheline_aligned;
/*
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index 61d042bbbf60..68449293c4b6 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -163,6 +163,7 @@ struct dccp_request_sock {
__u64 dreq_isr;
__u64 dreq_gsr;
__be32 dreq_service;
+ spinlock_t dreq_lock;
struct list_head dreq_featneg;
__u32 dreq_timestamp_echo;
__u32 dreq_timestamp_time;
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 5c720864db89..403a239b7aa1 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -415,6 +415,13 @@ union map_info *dm_get_rq_mapinfo(struct request *rq);
struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
+void dm_lock_md_type(struct mapped_device *md);
+void dm_unlock_md_type(struct mapped_device *md);
+void dm_set_md_type(struct mapped_device *md, unsigned type);
+unsigned dm_get_md_type(struct mapped_device *md);
+int dm_setup_md_queue(struct mapped_device *md);
+unsigned dm_table_get_type(struct dm_table *t);
+
/*
* Geometry functions.
*/
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index e9744202fa29..bc38b99a9fc9 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -35,6 +35,7 @@ enum hdmi_infoframe_type {
};
#define HDMI_IEEE_OUI 0x000c03
+#define HDMI_IEEE_OUI_HF 0xc45dd8
#define HDMI_INFOFRAME_HEADER_SIZE 4
#define HDMI_AVI_INFOFRAME_SIZE 13
#define HDMI_SPD_INFOFRAME_SIZE 25
@@ -78,6 +79,8 @@ enum hdmi_picture_aspect {
HDMI_PICTURE_ASPECT_NONE,
HDMI_PICTURE_ASPECT_4_3,
HDMI_PICTURE_ASPECT_16_9,
+ HDMI_PICTURE_ASPECT_64_27,
+ HDMI_PICTURE_ASPECT_256_135,
HDMI_PICTURE_ASPECT_RESERVED,
};
diff --git a/include/linux/io-pgtable-fast.h b/include/linux/io-pgtable-fast.h
index 029e11f9919b..6a56f0039f15 100644
--- a/include/linux/io-pgtable-fast.h
+++ b/include/linux/io-pgtable-fast.h
@@ -37,7 +37,7 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size);
#define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0xa
void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, u64 base,
- u64 end, bool skip_sync);
+ u64 start, u64 end, bool skip_sync);
void av8l_register_notify(struct notifier_block *nb);
#else /* !CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB */
@@ -46,6 +46,7 @@ void av8l_register_notify(struct notifier_block *nb);
static inline void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds,
u64 base,
+ u64 start,
u64 end,
bool skip_sync)
{
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index c4c25651ff21..55ae8b928411 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -137,6 +137,7 @@ enum iommu_attr {
DOMAIN_ATTR_PAGE_TABLE_IS_COHERENT,
DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT,
DOMAIN_ATTR_ENABLE_TTBR1,
+ DOMAIN_ATTR_CB_STALL_DISABLE,
DOMAIN_ATTR_MAX,
};
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 1129d68cb6cc..cc69810fcc32 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -18,6 +18,7 @@ struct ipv6_devconf {
__s32 dad_transmits;
__s32 rtr_solicits;
__s32 rtr_solicit_interval;
+ __s32 rtr_solicit_max_interval;
__s32 rtr_solicit_delay;
__s32 force_mld_version;
__s32 mldv1_unsolicited_report_interval;
@@ -36,6 +37,7 @@ struct ipv6_devconf {
__s32 accept_ra_rtr_pref;
__s32 rtr_probe_interval;
#ifdef CONFIG_IPV6_ROUTE_INFO
+ __s32 accept_ra_rt_info_min_plen;
__s32 accept_ra_rt_info_max_plen;
#endif
#endif
diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h
index 4b5a339970fa..1fe6e1709fa6 100644
--- a/include/linux/leds-qpnp-flash.h
+++ b/include/linux/leds-qpnp-flash.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,7 +18,6 @@
#define ENABLE_REGULATOR BIT(0)
#define DISABLE_REGULATOR BIT(1)
#define QUERY_MAX_CURRENT BIT(2)
-#define PRE_FLASH BIT(3)
#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(3, 0)
diff --git a/include/linux/log2.h b/include/linux/log2.h
index fd7ff3d91e6a..f38fae23bdac 100644
--- a/include/linux/log2.h
+++ b/include/linux/log2.h
@@ -16,12 +16,6 @@
#include <linux/bitops.h>
/*
- * deal with unrepresentable constant logarithms
- */
-extern __attribute__((const, noreturn))
-int ____ilog2_NaN(void);
-
-/*
* non-constant log of base 2 calculators
* - the arch may override these in asm/bitops.h if they can be implemented
* more efficiently than using fls() and fls64()
@@ -85,7 +79,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n)
#define ilog2(n) \
( \
__builtin_constant_p(n) ? ( \
- (n) < 1 ? ____ilog2_NaN() : \
+ (n) < 2 ? 0 : \
(n) & (1ULL << 63) ? 63 : \
(n) & (1ULL << 62) ? 62 : \
(n) & (1ULL << 61) ? 61 : \
@@ -148,10 +142,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n)
(n) & (1ULL << 4) ? 4 : \
(n) & (1ULL << 3) ? 3 : \
(n) & (1ULL << 2) ? 2 : \
- (n) & (1ULL << 1) ? 1 : \
- (n) & (1ULL << 0) ? 0 : \
- ____ilog2_NaN() \
- ) : \
+ 1 ) : \
(sizeof(n) <= 4) ? \
__ilog2_u32(n) : \
__ilog2_u64(n) \
diff --git a/include/linux/mdss_io_util.h b/include/linux/mdss_io_util.h
index 5b2587b28737..3cca007e618c 100644
--- a/include/linux/mdss_io_util.h
+++ b/include/linux/mdss_io_util.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -58,6 +58,8 @@ struct dss_vreg {
int post_on_sleep;
int pre_off_sleep;
int post_off_sleep;
+ bool lp_disable_allowed;
+ bool disabled;
};
struct dss_gpio {
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 186d2ab9cd13..3c10d4638646 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2146,6 +2146,17 @@ static inline void vm_stat_account(struct mm_struct *mm,
}
#endif /* CONFIG_PROC_FS */
+#ifdef CONFIG_PAGE_POISONING
+extern bool page_poisoning_enabled(void);
+extern void kernel_poison_pages(struct page *page, int numpages, int enable);
+extern bool page_is_poisoned(struct page *page);
+#else
+static inline bool page_poisoning_enabled(void) { return false; }
+static inline void kernel_poison_pages(struct page *page, int numpages,
+ int enable) { }
+static inline bool page_is_poisoned(struct page *page) { return false; }
+#endif
+
#ifdef CONFIG_DEBUG_PAGEALLOC
extern bool _debug_pagealloc_enabled;
extern void __kernel_map_pages(struct page *page, int numpages, int enable);
@@ -2295,7 +2306,6 @@ extern void copy_user_huge_page(struct page *dst, struct page *src,
#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_HUGETLBFS */
extern struct page_ext_operations debug_guardpage_ops;
-extern struct page_ext_operations page_poisoning_ops;
#ifdef CONFIG_DEBUG_PAGEALLOC
extern unsigned int _debug_guardpage_minorder;
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 31cc6f40baa5..8e76f46adde3 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -131,6 +131,9 @@ struct mmc_ext_csd {
#define MMC_BKOPS_URGENCY_MASK 0x3
u8 raw_bkops_status; /* 246 */
u8 raw_sectors[4]; /* 212 - 4 bytes */
+ u8 pre_eol_info; /* 267 */
+ u8 device_life_time_est_typ_a; /* 268 */
+ u8 device_life_time_est_typ_b; /* 269 */
u8 cmdq_depth; /* 307 */
u8 cmdq_support; /* 308 */
u8 barrier_support; /* 486 */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 9cd5afaef626..307a8e4be91f 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -282,6 +282,9 @@ struct _mmc_csd {
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */
#define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */
+#define EXT_CSD_PRE_EOL_INFO 267 /* RO */
+#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A 268 /* RO */
+#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B 269 /* RO */
#define EXT_CSD_CMDQ_DEPTH 307 /* RO */
#define EXT_CSD_CMDQ_SUPPORT 308 /* RO */
#define EXT_CSD_BARRIER_SUPPORT 486 /* RO */
diff --git a/include/linux/msm_smd_pkt.h b/include/linux/msm_smd_pkt.h
new file mode 100644
index 000000000000..c79933d27d4a
--- /dev/null
+++ b/include/linux/msm_smd_pkt.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2010,2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 __LINUX_MSM_SMD_PKT_H
+#define __LINUX_MSM_SMD_PKT_H
+
+#include <linux/ioctl.h>
+
+#define SMD_PKT_IOCTL_MAGIC (0xC2)
+
+#define SMD_PKT_IOCTL_BLOCKING_WRITE \
+ _IOR(SMD_PKT_IOCTL_MAGIC, 0, unsigned int)
+
+#endif /* __LINUX_MSM_SMD_PKT_H */
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 654bb97a3188..5bc4836af286 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -77,6 +77,12 @@ struct pmu_hw_events {
struct arm_pmu *percpu_pmu;
};
+enum armpmu_pmu_states {
+ ARM_PMU_STATE_OFF,
+ ARM_PMU_STATE_RUNNING,
+ ARM_PMU_STATE_GOING_DOWN,
+};
+
struct arm_pmu {
struct pmu pmu;
cpumask_t active_irqs;
@@ -101,6 +107,8 @@ struct arm_pmu {
void (*free_irq)(struct arm_pmu *);
int (*map_event)(struct perf_event *event);
int num_events;
+ int pmu_state;
+ int percpu_irq;
atomic_t active_events;
struct mutex reserve_mutex;
u64 max_period;
diff --git a/include/linux/pfn.h b/include/linux/pfn.h
index 7646637221f3..97f3e88aead4 100644
--- a/include/linux/pfn.h
+++ b/include/linux/pfn.h
@@ -9,5 +9,6 @@
#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
#define PFN_PHYS(x) ((phys_addr_t)(x) << PAGE_SHIFT)
+#define PHYS_PFN(x) ((unsigned long)((x) >> PAGE_SHIFT))
#endif
diff --git a/include/linux/poison.h b/include/linux/poison.h
index 317e16de09e5..199ffec4bdf3 100644
--- a/include/linux/poison.h
+++ b/include/linux/poison.h
@@ -30,7 +30,11 @@
#define TIMER_ENTRY_STATIC ((void *) 0x74737461)
/********** mm/debug-pagealloc.c **********/
+#ifdef CONFIG_PAGE_POISONING_ZERO
+#define PAGE_POISON 0x00
+#else
#define PAGE_POISON 0xaa
+#endif
/********** mm/slab.c **********/
/*
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
index 7254f4d16176..b025df568259 100644
--- a/include/linux/qpnp/qpnp-revid.h
+++ b/include/linux/qpnp/qpnp-revid.h
@@ -214,6 +214,11 @@
#define PM660L_V1P1_REV3 0x01
#define PM660L_V1P1_REV4 0x01
+#define PM660L_V2P0_REV1 0x00
+#define PM660L_V2P0_REV2 0x00
+#define PM660L_V2P0_REV3 0x00
+#define PM660L_V2P0_REV4 0x02
+
/* PMI8998 FAB_ID */
#define PMI8998_FAB_ID_SMIC 0x11
#define PMI8998_FAB_ID_GF 0x30
@@ -235,6 +240,9 @@
/* SMB1381 */
#define SMB1381_SUBTYPE 0x17
+/* SMB1355 */
+#define SMB1355_SUBTYPE 0x1C
+
struct pmic_revid_data {
u8 rev1;
u8 rev2;
diff --git a/include/linux/regulator/qpnp-labibb-regulator.h b/include/linux/regulator/qpnp-labibb-regulator.h
index 247069507fd9..33985afeb6e9 100644
--- a/include/linux/regulator/qpnp-labibb-regulator.h
+++ b/include/linux/regulator/qpnp-labibb-regulator.h
@@ -15,6 +15,7 @@
enum labibb_notify_event {
LAB_VREG_OK = 1,
+ LAB_VREG_NOT_OK,
};
int qpnp_labibb_notifier_register(struct notifier_block *nb);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 4b56a62a0a58..9cb0c1712792 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -3446,6 +3446,11 @@ static inline void inc_syscw(struct task_struct *tsk)
{
tsk->ioac.syscw++;
}
+
+static inline void inc_syscfs(struct task_struct *tsk)
+{
+ tsk->ioac.syscfs++;
+}
#else
static inline void add_rchar(struct task_struct *tsk, ssize_t amt)
{
@@ -3462,6 +3467,9 @@ static inline void inc_syscr(struct task_struct *tsk)
static inline void inc_syscw(struct task_struct *tsk)
{
}
+static inline void inc_syscfs(struct task_struct *tsk)
+{
+}
#endif
#ifndef TASK_SIZE_OF
diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h
index 4fcacd915d45..e77f648f9662 100644
--- a/include/linux/shrinker.h
+++ b/include/linux/shrinker.h
@@ -66,6 +66,7 @@ struct shrinker {
/* Flags */
#define SHRINKER_NUMA_AWARE (1 << 0)
#define SHRINKER_MEMCG_AWARE (1 << 1)
+#define SHRINKER_LMK (1 << 2)
extern int register_shrinker(struct shrinker *);
extern void unregister_shrinker(struct shrinker *);
diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h
index bdf855c2856f..2dd338fdf881 100644
--- a/include/linux/task_io_accounting.h
+++ b/include/linux/task_io_accounting.h
@@ -18,6 +18,8 @@ struct task_io_accounting {
u64 syscr;
/* # of write syscalls */
u64 syscw;
+ /* # of fsync syscalls */
+ u64 syscfs;
#endif /* CONFIG_TASK_XACCT */
#ifdef CONFIG_TASK_IO_ACCOUNTING
diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h
index 4d090f9ee608..1b505c804af3 100644
--- a/include/linux/task_io_accounting_ops.h
+++ b/include/linux/task_io_accounting_ops.h
@@ -96,6 +96,7 @@ static inline void task_chr_io_accounting_add(struct task_io_accounting *dst,
dst->wchar += src->wchar;
dst->syscr += src->syscr;
dst->syscw += src->syscw;
+ dst->syscfs += src->syscfs;
}
#else
static inline void task_chr_io_accounting_add(struct task_io_accounting *dst,
diff --git a/include/linux/timer.h b/include/linux/timer.h
index b1617e8932b2..1239c6ef949e 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -173,6 +173,9 @@ extern int mod_timer_pending(struct timer_list *timer, unsigned long expires);
extern int mod_timer_pinned(struct timer_list *timer, unsigned long expires);
extern void set_timer_slack(struct timer_list *time, int slack_hz);
+#ifdef CONFIG_SMP
+extern bool check_pending_deferrable_timers(int cpu);
+#endif
#define TIMER_NOT_PINNED 0
#define TIMER_PINNED 1
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 7e6e4665212f..a55f127d6836 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -371,14 +371,13 @@ struct usb_bus {
int devnum_next; /* Next open device number in
* round-robin allocation */
+ struct mutex devnum_next_mutex; /* devnum_next mutex */
struct usb_devmap devmap; /* device address allocation map */
struct usb_device *root_hub; /* Root hub */
struct usb_bus *hs_companion; /* Companion EHCI bus, if any */
struct list_head bus_list; /* list of busses */
- struct mutex usb_address0_mutex; /* unaddressed device mutex */
-
int bandwidth_allocated; /* on this bus: how much of the time
* reserved for periodic (intr/iso)
* requests is used, on average?
diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h
new file mode 100644
index 000000000000..f2322f3c74f7
--- /dev/null
+++ b/include/linux/usb/audio-v3.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This file holds USB constants and structures defined
+ * by the USB Device Class Definition for Audio Devices in version 3.0.
+ * Comments below reference relevant sections of the documents contained
+ * in http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip
+ */
+
+#ifndef __LINUX_USB_AUDIO_V3_H
+#define __LINUX_USB_AUDIO_V3_H
+
+#include <linux/types.h>
+
+#define UAC3_MIXER_UNIT_V3 0x05
+#define UAC3_FEATURE_UNIT_V3 0x07
+#define UAC3_CLOCK_SOURCE 0x0b
+
+#define BADD_MAXPSIZE_SYNC_MONO_16 0x0060
+#define BADD_MAXPSIZE_SYNC_MONO_24 0x0090
+#define BADD_MAXPSIZE_SYNC_STEREO_16 0x00c0
+#define BADD_MAXPSIZE_SYNC_STEREO_24 0x0120
+
+#define BADD_MAXPSIZE_ASYNC_MONO_16 0x0062
+#define BADD_MAXPSIZE_ASYNC_MONO_24 0x0093
+#define BADD_MAXPSIZE_ASYNC_STEREO_16 0x00c4
+#define BADD_MAXPSIZE_ASYNC_STEREO_24 0x0126
+
+#define BIT_RES_16_BIT 0x10
+#define BIT_RES_24_BIT 0x18
+
+#define SUBSLOTSIZE_16_BIT 0x02
+#define SUBSLOTSIZE_24_BIT 0x03
+
+#define BADD_SAMPLING_RATE 48000
+
+#define NUM_CHANNELS_MONO 1
+#define NUM_CHANNELS_STEREO 2
+#define BADD_CH_CONFIG_MONO 0
+#define BADD_CH_CONFIG_STEREO 3
+#define CLUSTER_ID_MONO 0x0001
+#define CLUSTER_ID_STEREO 0x0002
+
+#define FULL_ADC_PROFILE 0x01
+
+/* BADD Profile IDs */
+#define PROF_GENERIC_IO 0x20
+#define PROF_HEADPHONE 0x21
+#define PROF_SPEAKER 0x22
+#define PROF_MICROPHONE 0x23
+#define PROF_HEADSET 0x24
+#define PROF_HEADSET_ADAPTER 0x25
+#define PROF_SPEAKERPHONE 0x26
+
+/* BADD Entity IDs */
+#define BADD_OUT_TERM_ID_BAOF 0x03
+#define BADD_OUT_TERM_ID_BAIF 0x06
+#define BADD_IN_TERM_ID_BAOF 0x01
+#define BADD_IN_TERM_ID_BAIF 0x04
+#define BADD_FU_ID_BAOF 0x02
+#define BADD_FU_ID_BAIF 0x05
+#define BADD_CLOCK_SOURCE 0x09
+#define BADD_FU_ID_BAIOF 0x07
+#define BADD_MU_ID_BAIOF 0x08
+
+#define UAC_BIDIR_TERMINAL_HEADSET 0x0402
+#define UAC_BIDIR_TERMINAL_SPEAKERPHONE 0x0403
+
+#define NUM_BADD_DESCS 7
+
+struct uac3_input_terminal_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bTerminalID;
+ __u16 wTerminalType;
+ __u8 bAssocTerminal;
+ __u8 bCSourceID;
+ __u32 bmControls;
+ __u16 wClusterDescrID;
+ __u16 wExTerminalDescrID;
+ __u16 wConnectorsDescrID;
+ __u16 wTerminalDescrStr;
+} __packed;
+
+#define UAC3_DT_INPUT_TERMINAL_SIZE 0x14
+
+extern struct uac3_input_terminal_descriptor badd_baif_in_term_desc;
+extern struct uac3_input_terminal_descriptor badd_baof_in_term_desc;
+
+struct uac3_output_terminal_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bTerminalID;
+ __u16 wTerminalType;
+ __u8 bAssocTerminal;
+ __u8 bSourceID;
+ __u8 bCSourceID;
+ __u32 bmControls;
+ __u16 wExTerminalDescrID;
+ __u16 wConnectorsDescrID;
+ __u16 wTerminalDescrStr;
+} __packed;
+
+#define UAC3_DT_OUTPUT_TERMINAL_SIZE 0x13
+
+extern struct uac3_output_terminal_descriptor badd_baif_out_term_desc;
+extern struct uac3_output_terminal_descriptor badd_baof_out_term_desc;
+
+extern __u8 monoControls[];
+extern __u8 stereoControls[];
+extern __u8 badd_mu_src_ids[];
+
+struct uac3_mixer_unit_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bUnitID;
+ __u8 bNrInPins;
+ __u8 *baSourceID;
+ __u16 wClusterDescrID;
+ __u8 bmMixerControls;
+ __u32 bmControls;
+ __u16 wMixerDescrStr;
+} __packed;
+
+#define UAC3_DT_MIXER_UNIT_SIZE 0x10
+
+extern struct uac3_mixer_unit_descriptor badd_baiof_mu_desc;
+
+struct uac3_feature_unit_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bUnitID;
+ __u8 bSourceID;
+ __u8 *bmaControls;
+ __u16 wFeatureDescrStr;
+} __packed;
+
+extern struct uac3_feature_unit_descriptor badd_baif_fu_desc;
+extern struct uac3_feature_unit_descriptor badd_baof_fu_desc;
+extern struct uac3_feature_unit_descriptor badd_baiof_fu_desc;
+
+struct uac3_clock_source_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bClockID;
+ __u8 bmAttributes;
+ __u32 bmControls;
+ __u8 bReferenceTerminal;
+ __u16 wClockSourceStr;
+} __packed;
+
+#define UAC3_DT_CLOCK_SRC_SIZE 0x0c
+
+extern struct uac3_clock_source_descriptor badd_clock_desc;
+
+extern void *badd_desc_list[];
+
+#endif /* __LINUX_USB_AUDIO_V3_H */
diff --git a/include/linux/usb/ccid_desc.h b/include/linux/usb/ccid_desc.h
index 9a0c72650cd2..2e6dbb5afe71 100644
--- a/include/linux/usb/ccid_desc.h
+++ b/include/linux/usb/ccid_desc.h
@@ -86,27 +86,27 @@
* Table 5.1-1 Smart Card Device Class descriptors
*/
struct usb_ccid_class_descriptor {
- unsigned char bLength;
- unsigned char bDescriptorType;
- unsigned short bcdCCID;
- unsigned char bMaxSlotIndex;
- unsigned char bVoltageSupport;
- unsigned long dwProtocols;
- unsigned long dwDefaultClock;
- unsigned long dwMaximumClock;
- unsigned char bNumClockSupported;
- unsigned long dwDataRate;
- unsigned long dwMaxDataRate;
- unsigned char bNumDataRatesSupported;
- unsigned long dwMaxIFSD;
- unsigned long dwSynchProtocols;
- unsigned long dwMechanical;
- unsigned long dwFeatures;
- unsigned long dwMaxCCIDMessageLength;
- unsigned char bClassGetResponse;
- unsigned char bClassEnvelope;
- unsigned short wLcdLayout;
- unsigned char bPINSupport;
- unsigned char bMaxCCIDBusySlots;
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u16 bcdCCID;
+ __u8 bMaxSlotIndex;
+ __u8 bVoltageSupport;
+ __u32 dwProtocols;
+ __u32 dwDefaultClock;
+ __u32 dwMaximumClock;
+ __u8 bNumClockSupported;
+ __u32 dwDataRate;
+ __u32 dwMaxDataRate;
+ __u8 bNumDataRatesSupported;
+ __u32 dwMaxIFSD;
+ __u32 dwSynchProtocols;
+ __u32 dwMechanical;
+ __u32 dwFeatures;
+ __u32 dwMaxCCIDMessageLength;
+ __u8 bClassGetResponse;
+ __u8 bClassEnvelope;
+ __u16 wLcdLayout;
+ __u8 bPINSupport;
+ __u8 bMaxCCIDBusySlots;
} __packed;
#endif
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index cef429cf3dce..e888eb9a2eb9 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -180,6 +180,7 @@ struct usb_hcd {
* bandwidth_mutex should be dropped after a successful control message
* to the device, or resetting the bandwidth after a failed attempt.
*/
+ struct mutex *address0_mutex;
struct mutex *bandwidth_mutex;
struct usb_hcd *shared_hcd;
struct usb_hcd *primary_hcd;
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 1d0043dc34e4..de2a722fe3cf 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -50,4 +50,10 @@
/* device can't handle Link Power Management */
#define USB_QUIRK_NO_LPM BIT(10)
+/*
+ * Device reports its bInterval as linear frames instead of the
+ * USB 2.0 calculation.
+ */
+#define USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL BIT(11)
+
#endif /* __LINUX_USB_QUIRKS_H */
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index 75d0912aa459..762f1c51620c 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -41,6 +41,7 @@ struct msm_camera_sensor_slave_info32 {
uint8_t is_init_params_valid;
struct msm_sensor_init_params sensor_init_params;
enum msm_sensor_output_format_t output_format;
+ uint8_t bypass_video_node_creation;
};
struct msm_camera_csid_lut_params32 {
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 3275ddf9f00d..d540657819ef 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -1,8 +1,9 @@
#ifndef _ADDRCONF_H
#define _ADDRCONF_H
-#define MAX_RTR_SOLICITATIONS 3
+#define MAX_RTR_SOLICITATIONS -1 /* unlimited */
#define RTR_SOLICITATION_INTERVAL (4*HZ)
+#define RTR_SOLICITATION_MAX_INTERVAL (3600*HZ) /* 1 hour */
#define MIN_VALID_LIFETIME (2*3600) /* 2 hours */
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 1c8b6820b694..515352c6280a 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -201,6 +201,7 @@ struct inet6_dev {
struct ipv6_devstat stats;
struct timer_list rs_timer;
+ __s32 rs_interval; /* in jiffies */
__u8 rs_probes;
__u8 addr_gen_mode;
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 4d1c46aac331..c7b1dc713cdd 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -196,6 +196,7 @@ struct iscsi_conn {
struct iscsi_task *task; /* xmit task in progress */
/* xmit */
+ spinlock_t taskqueuelock; /* protects the next three lists */
struct list_head mgmtqueue; /* mgmt (control) xmit queue */
struct list_head cmdqueue; /* data-path cmd queue */
struct list_head requeue; /* tasks needing another run */
diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h
new file mode 100644
index 000000000000..9d993a17fb89
--- /dev/null
+++ b/include/soc/qcom/minidump.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MINIDUMP_H
+#define __MINIDUMP_H
+
+#define MAX_NAME_LENGTH 16
+/* md_region - Minidump table entry
+ * @name: Entry name, Minidump will dump binary with this name.
+ * @id: Entry ID, used only for SDI dumps.
+ * @virt_addr: Address of the entry.
+ * @phys_addr: Physical address of the entry to dump.
+ * @size: Number of byte to dump from @address location
+ * it should be 4 byte aligned.
+ */
+struct md_region {
+ char name[MAX_NAME_LENGTH];
+ u32 id;
+ u64 virt_addr;
+ u64 phys_addr;
+ u64 size;
+};
+
+/* Register an entry in Minidump table
+ * Returns:
+ * Zero: on successful addition
+ * Negetive error number on failures
+ */
+#ifdef CONFIG_QCOM_MINIDUMP
+extern int msm_minidump_add_region(const struct md_region *entry);
+extern bool msm_minidump_enabled(void);
+#else
+static inline int msm_minidump_add_region(const struct md_region *entry)
+{
+ return -ENODEV;
+}
+static inline bool msm_minidump_enabled(void) { return false; }
+#endif
+#endif
diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
index ac36df5769ee..dfb0280d146c 100644
--- a/include/soc/qcom/socinfo.h
+++ b/include/soc/qcom/socinfo.h
@@ -90,8 +90,8 @@
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8929")
#define early_machine_is_msm8998() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8998")
-#define early_machine_is_apq8998() \
- of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apq8998")
+#define early_machine_is_apq8098() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apq8098")
#define early_machine_is_msmhamster() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmhamster")
#define early_machine_is_sdm660() \
@@ -140,7 +140,7 @@
#define early_machine_is_msm8976() 0
#define early_machine_is_msm8929() 0
#define early_machine_is_msm8998() 0
-#define early_machine_is_apq8998() 0
+#define early_machine_is_apq8098() 0
#define early_machine_is_msmhamster() 0
#define early_machine_is_sdm660() 0
#define early_machine_is_sda660() 0
diff --git a/include/soc/qcom/subsystem_restart.h b/include/soc/qcom/subsystem_restart.h
index 763eaa9ad918..59749210001a 100644
--- a/include/soc/qcom/subsystem_restart.h
+++ b/include/soc/qcom/subsystem_restart.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
struct subsys_device;
+extern struct bus_type subsys_bus_type;
enum {
RESET_SOC = 0,
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index a5ba1496eef7..bcb53faaf678 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -5057,6 +5057,7 @@ struct asm_amrwbplus_fmt_blk_v2 {
#define ASM_MEDIA_FMT_VORBIS 0x00010C15
#define ASM_MEDIA_FMT_APE 0x00012F32
#define ASM_MEDIA_FMT_DSD 0x00012F3E
+#define ASM_MEDIA_FMT_TRUEHD 0x00013215
/* Media format ID for adaptive transform acoustic coding. This
* ID is used by the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED command
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
index 42d048f24e12..f3443b79ba51 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -164,4 +164,6 @@ int adm_get_sound_focus(int port_id, int copp_idx,
struct sound_focus_param *soundFocusData);
int adm_get_source_tracking(int port_id, int copp_idx,
struct source_tracking_param *sourceTrackingData);
+int adm_swap_speaker_channels(int port_id, int copp_idx, int sample_rate,
+ bool spk_swap);
#endif /* __Q6_ADM_V2_H__ */
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 29707b26644a..41fcdeb50831 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -55,6 +55,7 @@
#define FORMAT_DSD 0x001d
#define FORMAT_APTX 0x001e
#define FORMAT_GEN_COMPR 0x001f
+#define FORMAT_TRUEHD 0x0020
#define ENCDEC_SBCBITRATE 0x0001
#define ENCDEC_IMMEDIATE_DECODE 0x0002
diff --git a/include/trace/events/android_fs.h b/include/trace/events/android_fs.h
new file mode 100644
index 000000000000..49509533d3fa
--- /dev/null
+++ b/include/trace/events/android_fs.h
@@ -0,0 +1,65 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM android_fs
+
+#if !defined(_TRACE_ANDROID_FS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ANDROID_FS_H
+
+#include <linux/tracepoint.h>
+#include <trace/events/android_fs_template.h>
+
+DEFINE_EVENT(android_fs_data_start_template, android_fs_dataread_start,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes,
+ pid_t pid, char *pathname, char *command),
+ TP_ARGS(inode, offset, bytes, pid, pathname, command));
+
+DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes),
+ TP_ARGS(inode, offset, bytes));
+
+DEFINE_EVENT(android_fs_data_start_template, android_fs_datawrite_start,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes,
+ pid_t pid, char *pathname, char *command),
+ TP_ARGS(inode, offset, bytes, pid, pathname, command));
+
+DEFINE_EVENT(android_fs_data_end_template, android_fs_datawrite_end,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes),
+ TP_ARGS(inode, offset, bytes));
+
+#endif /* _TRACE_ANDROID_FS_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
+
+#ifndef ANDROID_FSTRACE_GET_PATHNAME
+#define ANDROID_FSTRACE_GET_PATHNAME
+
+/* Sizes an on-stack array, so careful if sizing this up ! */
+#define MAX_TRACE_PATHBUF_LEN 256
+
+static inline char *
+android_fstrace_get_pathname(char *buf, int buflen, struct inode *inode)
+{
+ char *path;
+ struct dentry *d;
+
+ /*
+ * d_obtain_alias() will either iput() if it locates an existing
+ * dentry or transfer the reference to the new dentry created.
+ * So get an extra reference here.
+ */
+ ihold(inode);
+ d = d_obtain_alias(inode);
+ if (likely(!IS_ERR(d))) {
+ path = dentry_path_raw(d, buf, buflen);
+ if (unlikely(IS_ERR(path))) {
+ strcpy(buf, "ERROR");
+ path = buf;
+ }
+ dput(d);
+ } else {
+ strcpy(buf, "ERROR");
+ path = buf;
+ }
+ return path;
+}
+#endif
diff --git a/include/trace/events/android_fs_template.h b/include/trace/events/android_fs_template.h
new file mode 100644
index 000000000000..b23d17b56c63
--- /dev/null
+++ b/include/trace/events/android_fs_template.h
@@ -0,0 +1,64 @@
+#if !defined(_TRACE_ANDROID_FS_TEMPLATE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ANDROID_FS_TEMPLATE_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(android_fs_data_start_template,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes,
+ pid_t pid, char *pathname, char *command),
+ TP_ARGS(inode, offset, bytes, pid, pathname, command),
+ TP_STRUCT__entry(
+ __string(pathbuf, pathname);
+ __field(loff_t, offset);
+ __field(int, bytes);
+ __field(loff_t, i_size);
+ __string(cmdline, command);
+ __field(pid_t, pid);
+ __field(ino_t, ino);
+ ),
+ TP_fast_assign(
+ {
+ /*
+ * Replace the spaces in filenames and cmdlines
+ * because this screws up the tooling that parses
+ * the traces.
+ */
+ __assign_str(pathbuf, pathname);
+ (void)strreplace(__get_str(pathbuf), ' ', '_');
+ __entry->offset = offset;
+ __entry->bytes = bytes;
+ __entry->i_size = i_size_read(inode);
+ __assign_str(cmdline, command);
+ (void)strreplace(__get_str(cmdline), ' ', '_');
+ __entry->pid = pid;
+ __entry->ino = inode->i_ino;
+ }
+ ),
+ TP_printk("entry_name %s, offset %llu, bytes %d, cmdline %s,"
+ " pid %d, i_size %llu, ino %lu",
+ __get_str(pathbuf), __entry->offset, __entry->bytes,
+ __get_str(cmdline), __entry->pid, __entry->i_size,
+ (unsigned long) __entry->ino)
+);
+
+DECLARE_EVENT_CLASS(android_fs_data_end_template,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes),
+ TP_ARGS(inode, offset, bytes),
+ TP_STRUCT__entry(
+ __field(ino_t, ino);
+ __field(loff_t, offset);
+ __field(int, bytes);
+ ),
+ TP_fast_assign(
+ {
+ __entry->ino = inode->i_ino;
+ __entry->offset = offset;
+ __entry->bytes = bytes;
+ }
+ ),
+ TP_printk("ino %lu, offset %llu, bytes %d",
+ (unsigned long) __entry->ino,
+ __entry->offset, __entry->bytes)
+);
+
+#endif /* _TRACE_ANDROID_FS_TEMPLATE_H */
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 09c22caf34dd..1c5e74f1ea39 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -73,7 +73,8 @@
#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14)
#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14)
#define DRM_MODE_FLAG_SEAMLESS (1<<19)
-
+#define DRM_MODE_FLAG_SUPPORTS_RGB (1<<20)
+#define DRM_MODE_FLAG_SUPPORTS_YUV (1<<21)
/* DPMS flags */
/* bit compatible with the xorg definitions. */
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index 8baf2bf6df2e..c974714a9abe 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -152,10 +152,13 @@ struct drm_msm_gem_submit_reloc {
* this buffer in the first-level ringbuffer
* CTX_RESTORE_BUF - only executed if there has been a GPU context
* switch since the last SUBMIT ioctl
+ * PROFILE_BUF - A profiling buffer written to by both GPU and CPU.
*/
#define MSM_SUBMIT_CMD_BUF 0x0001
#define MSM_SUBMIT_CMD_IB_TARGET_BUF 0x0002
#define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003
+#define MSM_SUBMIT_CMD_PROFILE_BUF 0x0004
+
struct drm_msm_gem_submit_cmd {
__u32 type; /* in, one of MSM_SUBMIT_CMD_x */
__u32 submit_idx; /* in, index of submit_bo cmdstream buffer */
@@ -207,6 +210,14 @@ struct drm_msm_gem_submit {
__u64 __user cmds; /* in, ptr to array of submit_cmd's */
};
+struct drm_msm_gem_submit_profile_buffer {
+ __s64 queue_time; /* out, Ringbuffer queue time (seconds) */
+ __s64 submit_time; /* out, Ringbuffer submission time (seconds) */
+ __u64 ticks_queued; /* out, GPU ticks at ringbuffer submission */
+ __u64 ticks_submitted; /* out, GPU ticks before cmdstream execution*/
+ __u64 ticks_retired; /* out, GPU ticks after cmdstream execution */
+};
+
/* The normal way to synchronize with the GPU is just to CPU_PREP on
* a buffer if you need to access it from the CPU (other cmdstream
* submission from same or other contexts, PAGE_FLIP ioctl, etc, all
diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h
index 51f891fb1b18..7668b5791c91 100644
--- a/include/uapi/linux/android/binder.h
+++ b/include/uapi/linux/android/binder.h
@@ -132,6 +132,7 @@ enum {
/* struct binder_fd_array_object - object describing an array of fds in a buffer
* @hdr: common header structure
+ * @pad: padding to ensure correct alignment
* @num_fds: number of file descriptors in the buffer
* @parent: index in offset array to buffer holding the fd array
* @parent_offset: start offset of fd array in the buffer
@@ -152,6 +153,7 @@ enum {
*/
struct binder_fd_array_object {
struct binder_object_header hdr;
+ __u32 pad;
binder_size_t num_fds;
binder_size_t parent;
binder_size_t parent_offset;
diff --git a/include/uapi/linux/hbtp_input.h b/include/uapi/linux/hbtp_input.h
index 9173c2ab72ed..3b124ffcdcf3 100644
--- a/include/uapi/linux/hbtp_input.h
+++ b/include/uapi/linux/hbtp_input.h
@@ -6,6 +6,8 @@
#define HBTP_MAX_FINGER 20
#define HBTP_ABS_MT_FIRST ABS_MT_TOUCH_MAJOR
#define HBTP_ABS_MT_LAST ABS_MT_TOOL_Y
+#define MAX_ROI_SIZE 144
+#define MAX_ACCEL_SIZE 128
#define HBTP_EVENT_TYPE_DISPLAY "EVENT_TYPE=HBTP_DISPLAY"
@@ -20,6 +22,11 @@ struct hbtp_input_touch {
__s32 orientation;
};
+struct hbtp_sensor_data {
+ __s16 accelBuffer[MAX_ACCEL_SIZE];
+ __s16 ROI[MAX_ROI_SIZE];
+};
+
struct hbtp_input_mt {
__s32 num_touches;
struct hbtp_input_touch touches[HBTP_MAX_FINGER];
@@ -68,6 +75,8 @@ enum hbtp_afe_power_ctrl {
enum hbtp_afe_signal)
#define HBTP_SET_POWER_CTRL _IOW(HBTP_INPUT_IOCTL_BASE, 206, \
enum hbtp_afe_power_ctrl)
+#define HBTP_SET_SENSORDATA _IOW(HBTP_INPUT_IOCTL_BASE, 207, \
+ struct hbtp_sensor_data)
#endif /* _UAPI_HBTP_INPUT_H */
diff --git a/include/uapi/linux/hw_breakpoint.h b/include/uapi/linux/hw_breakpoint.h
index b04000a2296a..2b65efd19a46 100644
--- a/include/uapi/linux/hw_breakpoint.h
+++ b/include/uapi/linux/hw_breakpoint.h
@@ -4,7 +4,11 @@
enum {
HW_BREAKPOINT_LEN_1 = 1,
HW_BREAKPOINT_LEN_2 = 2,
+ HW_BREAKPOINT_LEN_3 = 3,
HW_BREAKPOINT_LEN_4 = 4,
+ HW_BREAKPOINT_LEN_5 = 5,
+ HW_BREAKPOINT_LEN_6 = 6,
+ HW_BREAKPOINT_LEN_7 = 7,
HW_BREAKPOINT_LEN_8 = 8,
};
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 35bf6f1b186d..f0c24f24f0c3 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -176,6 +176,16 @@ enum {
DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT,
DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN,
DEVCONF_ACCEPT_RA_PREFIX_ROUTE,
+ DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
+ DEVCONF_DROP_UNSOLICITED_NA,
+ DEVCONF_KEEP_ADDR_ON_DOWN,
+ DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
+ DEVCONF_SEG6_ENABLED,
+ DEVCONF_SEG6_REQUIRE_HMAC,
+ DEVCONF_ENHANCED_DAD,
+ DEVCONF_ADDR_GEN_MODE,
+ DEVCONF_DISABLE_POLICY,
+ DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
DEVCONF_MAX
};
diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h
index 843e02711aa7..5fd58dfb153e 100644
--- a/include/uapi/linux/msm_kgsl.h
+++ b/include/uapi/linux/msm_kgsl.h
@@ -67,6 +67,8 @@
#define KGSL_CONTEXT_TYPE_RS 4
#define KGSL_CONTEXT_TYPE_UNKNOWN 0x1E
+#define KGSL_CONTEXT_INVALIDATE_ON_FAULT 0x10000000
+
#define KGSL_CONTEXT_INVALID 0xffffffff
/*
diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index ee4581d652f4..a9d1772199bf 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -107,8 +107,10 @@ struct nlmsgerr {
#define NETLINK_PKTINFO 3
#define NETLINK_BROADCAST_ERROR 4
#define NETLINK_NO_ENOBUFS 5
+#ifndef __KERNEL__
#define NETLINK_RX_RING 6
#define NETLINK_TX_RING 7
+#endif
#define NETLINK_LISTEN_ALL_NSID 8
#define NETLINK_LIST_MEMBERSHIPS 9
#define NETLINK_CAP_ACK 10
@@ -134,6 +136,7 @@ struct nl_mmap_hdr {
__u32 nm_gid;
};
+#ifndef __KERNEL__
enum nl_mmap_status {
NL_MMAP_STATUS_UNUSED,
NL_MMAP_STATUS_RESERVED,
@@ -145,6 +148,7 @@ enum nl_mmap_status {
#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO
#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
+#endif
#define NET_MAJOR 36 /* Major 36 is reserved for networking */
diff --git a/include/uapi/linux/netlink_diag.h b/include/uapi/linux/netlink_diag.h
index f2159d30d1f5..d79399394b46 100644
--- a/include/uapi/linux/netlink_diag.h
+++ b/include/uapi/linux/netlink_diag.h
@@ -48,6 +48,8 @@ enum {
#define NDIAG_SHOW_MEMINFO 0x00000001 /* show memory info of a socket */
#define NDIAG_SHOW_GROUPS 0x00000002 /* show groups of a netlink socket */
+#ifndef __KERNEL__
#define NDIAG_SHOW_RING_CFG 0x00000004 /* show ring configuration */
+#endif
#endif
diff --git a/include/uapi/linux/packet_diag.h b/include/uapi/linux/packet_diag.h
index d08c63f3dd6f..0c5d5dd61b6a 100644
--- a/include/uapi/linux/packet_diag.h
+++ b/include/uapi/linux/packet_diag.h
@@ -64,7 +64,7 @@ struct packet_diag_mclist {
__u32 pdmc_count;
__u16 pdmc_type;
__u16 pdmc_alen;
- __u8 pdmc_addr[MAX_ADDR_LEN];
+ __u8 pdmc_addr[32]; /* MAX_ADDR_LEN */
};
struct packet_diag_ring {
diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h
index 33c6cde783ce..01eb22ca6b3d 100644
--- a/include/uapi/linux/sysctl.h
+++ b/include/uapi/linux/sysctl.h
@@ -571,6 +571,7 @@ enum {
NET_IPV6_PROXY_NDP=23,
NET_IPV6_ACCEPT_SOURCE_ROUTE=25,
NET_IPV6_ACCEPT_RA_FROM_LOCAL=26,
+ NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27,
__NET_IPV6_MAX
};
diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h
index d2314be4f0c0..c6f5b096c594 100644
--- a/include/uapi/linux/usb/audio.h
+++ b/include/uapi/linux/usb/audio.h
@@ -26,6 +26,7 @@
/* bInterfaceProtocol values to denote the version of the standard used */
#define UAC_VERSION_1 0x00
#define UAC_VERSION_2 0x20
+#define UAC_VERSION_3 0x30
/* A.2 Audio Interface Subclass Codes */
#define USB_SUBCLASS_AUDIOCONTROL 0x01
diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h
index ad0825e33217..83927c614e91 100644
--- a/include/uapi/media/msm_camsensor_sdk.h
+++ b/include/uapi/media/msm_camsensor_sdk.h
@@ -48,6 +48,8 @@
#define MSM_EEPROM_MEMORY_MAP_MAX_SIZE 80
#define MSM_EEPROM_MAX_MEM_MAP_CNT 8
+#define MSM_SENSOR_BYPASS_VIDEO_NODE 1
+
enum msm_sensor_camera_id_t {
CAMERA_0,
CAMERA_1,
@@ -300,6 +302,7 @@ struct msm_camera_sensor_slave_info {
unsigned char is_init_params_valid;
struct msm_sensor_init_params sensor_init_params;
enum msm_sensor_output_format_t output_format;
+ uint8_t bypass_video_node_creation;
};
struct msm_camera_i2c_reg_array {
diff --git a/include/uapi/media/msmb_ispif.h b/include/uapi/media/msmb_ispif.h
index 3720056aa28d..c3a6e006b2ff 100644
--- a/include/uapi/media/msmb_ispif.h
+++ b/include/uapi/media/msmb_ispif.h
@@ -36,7 +36,6 @@ enum msm_ispif_intftype {
#define RDI1_MASK (1 << RDI1)
#define RDI2_MASK (1 << RDI2)
-
enum msm_ispif_vc {
VC0,
VC1,
@@ -102,10 +101,17 @@ struct msm_ispif_params_entry {
uint16_t crop_end_pixel;
};
+struct msm_ispif_right_param_entry {
+ enum msm_ispif_cid cids[MAX_CID_CH_PARAM_ENTRY];
+ enum msm_ispif_csid csid;
+};
+
struct msm_ispif_param_data_ext {
uint32_t num;
struct msm_ispif_params_entry entries[MAX_PARAM_ENTRIES];
struct msm_ispif_pack_cfg pack_cfg[CID_MAX];
+ struct msm_ispif_right_param_entry right_entries[MAX_PARAM_ENTRIES];
+ uint32_t stereo_enable;
};
struct msm_ispif_param_data {
@@ -157,6 +163,8 @@ struct ispif_cfg_data_ext {
#define ISPIF_RDI_PACK_MODE_SUPPORT 1
+#define ISPIF_3D_SUPPORT 1
+
#define VIDIOC_MSM_ISPIF_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data)
diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h
index 6a297ad6d380..f4028e805cc7 100644
--- a/include/uapi/sound/compress_params.h
+++ b/include/uapi/sound/compress_params.h
@@ -104,7 +104,8 @@
#define SND_AUDIOCODEC_APE ((__u32) 0x00000020)
#define SND_AUDIOCODEC_DSD ((__u32) 0x00000021)
#define SND_AUDIOCODEC_APTX ((__u32) 0x00000022)
-#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_APTX
+#define SND_AUDIOCODEC_TRUEHD ((__u32) 0x00000023)
+#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_TRUEHD
/*
* Profile and modes are listed with bit masks. This allows for a
* more compact representation of fields that will not evolve
diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c
index ecda58df9a19..bce1c2fbb915 100644
--- a/init/do_mounts_dm.c
+++ b/init/do_mounts_dm.c
@@ -5,13 +5,17 @@
*
* This file is released under the GPL.
*/
+#include <linux/async.h>
+#include <linux/ctype.h>
#include <linux/device-mapper.h>
#include <linux/fs.h>
#include <linux/string.h>
+#include <linux/delay.h>
#include "do_mounts.h"
-#include "../drivers/md/dm.h"
+#define DM_MAX_DEVICES 256
+#define DM_MAX_TARGETS 256
#define DM_MAX_NAME 32
#define DM_MAX_UUID 129
#define DM_NO_UUID "none"
@@ -19,14 +23,47 @@
#define DM_MSG_PREFIX "init"
/* Separators used for parsing the dm= argument. */
-#define DM_FIELD_SEP ' '
-#define DM_LINE_SEP ','
+#define DM_FIELD_SEP " "
+#define DM_LINE_SEP ","
+#define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP
/*
* When the device-mapper and any targets are compiled into the kernel
- * (not a module), one target may be created and used as the root device at
- * boot time with the parameters given with the boot line dm=...
- * The code for that is here.
+ * (not a module), one or more device-mappers may be created and used
+ * as the root device at boot time with the parameters given with the
+ * boot line dm=...
+ *
+ * Multiple device-mappers can be stacked specifing the number of
+ * devices. A device can have multiple targets if the the number of
+ * targets is specified.
+ *
+ * TODO(taysom:defect 32847)
+ * In the future, the <num> field will be mandatory.
+ *
+ * <device> ::= [<num>] <device-mapper>+
+ * <device-mapper> ::= <head> "," <target>+
+ * <head> ::= <name> <uuid> <mode> [<num>]
+ * <target> ::= <start> <length> <type> <options> ","
+ * <mode> ::= "ro" | "rw"
+ * <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none"
+ * <type> ::= "verity" | "bootcache" | ...
+ *
+ * Example:
+ * 2 vboot none ro 1,
+ * 0 1768000 bootcache
+ * device=aa55b119-2a47-8c45-946a-5ac57765011f+1
+ * signature=76e9be054b15884a9fa85973e9cb274c93afadb6
+ * cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000,
+ * vroot none ro 1,
+ * 0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1
+ * root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6
+ * salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe
+ *
+ * Notes:
+ * 1. uuid is a label for the device and we set it to "none".
+ * 2. The <num> field will be optional initially and assumed to be 1.
+ * Once all the scripts that set these fields have been set, it will
+ * be made mandatory.
*/
struct dm_setup_target {
@@ -38,381 +75,388 @@ struct dm_setup_target {
struct dm_setup_target *next;
};
-static struct {
+struct dm_device {
int minor;
int ro;
char name[DM_MAX_NAME];
char uuid[DM_MAX_UUID];
- char *targets;
+ unsigned long num_targets;
struct dm_setup_target *target;
int target_count;
+ struct dm_device *next;
+};
+
+struct dm_option {
+ char *start;
+ char *next;
+ size_t len;
+ char delim;
+};
+
+static struct {
+ unsigned long num_devices;
+ char *str;
} dm_setup_args __initdata;
static __initdata int dm_early_setup;
-static size_t __init get_dm_option(char *str, char **next, char sep)
+static int __init get_dm_option(struct dm_option *opt, const char *accept)
{
- size_t len = 0;
- char *endp = NULL;
+ char *str = opt->next;
+ char *endp;
if (!str)
return 0;
- endp = strchr(str, sep);
+ str = skip_spaces(str);
+ opt->start = str;
+ endp = strpbrk(str, accept);
if (!endp) { /* act like strchrnul */
- len = strlen(str);
- endp = str + len;
+ opt->len = strlen(str);
+ endp = str + opt->len;
} else {
- len = endp - str;
+ opt->len = endp - str;
}
-
- if (endp == str)
- return 0;
-
- if (!next)
- return len;
-
+ opt->delim = *endp;
if (*endp == 0) {
/* Don't advance past the nul. */
- *next = endp;
+ opt->next = endp;
} else {
- *next = endp + 1;
+ opt->next = endp + 1;
}
- return len;
-}
-
-static int __init dm_setup_args_init(void)
-{
- dm_setup_args.minor = 0;
- dm_setup_args.ro = 0;
- dm_setup_args.target = NULL;
- dm_setup_args.target_count = 0;
- return 0;
+ return opt->len != 0;
}
-static int __init dm_setup_cleanup(void)
+static int __init dm_setup_cleanup(struct dm_device *devices)
{
- struct dm_setup_target *target = dm_setup_args.target;
- struct dm_setup_target *old_target = NULL;
- while (target) {
- kfree(target->type);
- kfree(target->params);
- old_target = target;
- target = target->next;
- kfree(old_target);
- dm_setup_args.target_count--;
+ struct dm_device *dev = devices;
+
+ while (dev) {
+ struct dm_device *old_dev = dev;
+ struct dm_setup_target *target = dev->target;
+ while (target) {
+ struct dm_setup_target *old_target = target;
+ kfree(target->type);
+ kfree(target->params);
+ target = target->next;
+ kfree(old_target);
+ dev->target_count--;
+ }
+ BUG_ON(dev->target_count);
+ dev = dev->next;
+ kfree(old_dev);
}
- BUG_ON(dm_setup_args.target_count);
return 0;
}
-static char * __init dm_setup_parse_device_args(char *str)
+static char * __init dm_parse_device(struct dm_device *dev, char *str)
{
- char *next = NULL;
- size_t len = 0;
+ struct dm_option opt;
+ size_t len;
/* Grab the logical name of the device to be exported to udev */
- len = get_dm_option(str, &next, DM_FIELD_SEP);
- if (!len) {
+ opt.next = str;
+ if (!get_dm_option(&opt, DM_FIELD_SEP)) {
DMERR("failed to parse device name");
goto parse_fail;
}
- len = min(len + 1, sizeof(dm_setup_args.name));
- strlcpy(dm_setup_args.name, str, len); /* includes nul */
- str = skip_spaces(next);
+ len = min(opt.len + 1, sizeof(dev->name));
+ strlcpy(dev->name, opt.start, len); /* includes nul */
/* Grab the UUID value or "none" */
- len = get_dm_option(str, &next, DM_FIELD_SEP);
- if (!len) {
+ if (!get_dm_option(&opt, DM_FIELD_SEP)) {
DMERR("failed to parse device uuid");
goto parse_fail;
}
- len = min(len + 1, sizeof(dm_setup_args.uuid));
- strlcpy(dm_setup_args.uuid, str, len);
- str = skip_spaces(next);
+ len = min(opt.len + 1, sizeof(dev->uuid));
+ strlcpy(dev->uuid, opt.start, len);
/* Determine if the table/device will be read only or read-write */
- if (!strncmp("ro,", str, 3)) {
- dm_setup_args.ro = 1;
- } else if (!strncmp("rw,", str, 3)) {
- dm_setup_args.ro = 0;
+ get_dm_option(&opt, DM_ANY_SEP);
+ if (!strncmp("ro", opt.start, opt.len)) {
+ dev->ro = 1;
+ } else if (!strncmp("rw", opt.start, opt.len)) {
+ dev->ro = 0;
} else {
DMERR("failed to parse table mode");
goto parse_fail;
}
- str = skip_spaces(str + 3);
- return str;
+ /* Optional number field */
+ /* XXX: The <num> field will be mandatory in the next round */
+ if (opt.delim == DM_FIELD_SEP[0]) {
+ if (!get_dm_option(&opt, DM_LINE_SEP))
+ return NULL;
+ dev->num_targets = simple_strtoul(opt.start, NULL, 10);
+ } else {
+ dev->num_targets = 1;
+ }
+ if (dev->num_targets > DM_MAX_TARGETS) {
+ DMERR("too many targets %lu > %d",
+ dev->num_targets, DM_MAX_TARGETS);
+ }
+ return opt.next;
parse_fail:
return NULL;
}
-static void __init dm_substitute_devices(char *str, size_t str_len)
+static char * __init dm_parse_targets(struct dm_device *dev, char *str)
{
- char *candidate = str;
- char *candidate_end = str;
- char old_char;
- size_t len = 0;
- dev_t dev;
-
- if (str_len < 3)
- return;
-
- while (str && *str) {
- candidate = strchr(str, '/');
- if (!candidate)
- break;
-
- /* Avoid embedded slashes */
- if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) {
- str = strchr(candidate, DM_FIELD_SEP);
- continue;
- }
-
- len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP);
- str = skip_spaces(candidate_end);
- if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */
- continue;
-
- /* Temporarily terminate with a nul */
- if (*candidate_end)
- candidate_end--;
- old_char = *candidate_end;
- *candidate_end = '\0';
-
- DMDEBUG("converting candidate device '%s' to dev_t", candidate);
- /* Use the boot-time specific device naming */
- dev = name_to_dev_t(candidate);
- *candidate_end = old_char;
-
- DMDEBUG(" -> %u", dev);
- /* No suitable replacement found */
- if (!dev)
- continue;
-
- /* Rewrite the /dev/path as a major:minor */
- len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev));
- if (!len) {
- DMERR("error substituting device major/minor.");
- break;
- }
- candidate += len;
- /* Pad out with spaces (fixing our nul) */
- while (candidate < candidate_end)
- *(candidate++) = DM_FIELD_SEP;
- }
-}
-
-static int __init dm_setup_parse_targets(char *str)
-{
- char *next = NULL;
- size_t len = 0;
- struct dm_setup_target **target = NULL;
+ struct dm_option opt;
+ struct dm_setup_target **target = &dev->target;
+ unsigned long num_targets = dev->num_targets;
+ unsigned long i;
/* Targets are defined as per the table format but with a
* comma as a newline separator. */
- target = &dm_setup_args.target;
- while (str && *str) {
+ opt.next = str;
+ for (i = 0; i < num_targets; i++) {
*target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL);
if (!*target) {
- DMERR("failed to allocate memory for target %d",
- dm_setup_args.target_count);
+ DMERR("failed to allocate memory for target %s<%ld>",
+ dev->name, i);
goto parse_fail;
}
- dm_setup_args.target_count++;
+ dev->target_count++;
- (*target)->begin = simple_strtoull(str, &next, 10);
- if (!next || *next != DM_FIELD_SEP) {
- DMERR("failed to parse starting sector for target %d",
- dm_setup_args.target_count - 1);
+ if (!get_dm_option(&opt, DM_FIELD_SEP)) {
+ DMERR("failed to parse starting sector"
+ " for target %s<%ld>", dev->name, i);
goto parse_fail;
}
- str = skip_spaces(next + 1);
+ (*target)->begin = simple_strtoull(opt.start, NULL, 10);
- (*target)->length = simple_strtoull(str, &next, 10);
- if (!next || *next != DM_FIELD_SEP) {
- DMERR("failed to parse length for target %d",
- dm_setup_args.target_count - 1);
+ if (!get_dm_option(&opt, DM_FIELD_SEP)) {
+ DMERR("failed to parse length for target %s<%ld>",
+ dev->name, i);
goto parse_fail;
}
- str = skip_spaces(next + 1);
-
- len = get_dm_option(str, &next, DM_FIELD_SEP);
- if (!len ||
- !((*target)->type = kstrndup(str, len, GFP_KERNEL))) {
- DMERR("failed to parse type for target %d",
- dm_setup_args.target_count - 1);
+ (*target)->length = simple_strtoull(opt.start, NULL, 10);
+
+ if (get_dm_option(&opt, DM_FIELD_SEP))
+ (*target)->type = kstrndup(opt.start, opt.len,
+ GFP_KERNEL);
+ if (!((*target)->type)) {
+ DMERR("failed to parse type for target %s<%ld>",
+ dev->name, i);
goto parse_fail;
}
- str = skip_spaces(next);
-
- len = get_dm_option(str, &next, DM_LINE_SEP);
- if (!len ||
- !((*target)->params = kstrndup(str, len, GFP_KERNEL))) {
- DMERR("failed to parse params for target %d",
- dm_setup_args.target_count - 1);
+ if (get_dm_option(&opt, DM_LINE_SEP))
+ (*target)->params = kstrndup(opt.start, opt.len,
+ GFP_KERNEL);
+ if (!((*target)->params)) {
+ DMERR("failed to parse params for target %s<%ld>",
+ dev->name, i);
goto parse_fail;
}
- str = skip_spaces(next);
-
- /* Before moving on, walk through the copied target and
- * attempt to replace all /dev/xxx with the major:minor number.
- * It may not be possible to resolve them traditionally at
- * boot-time. */
- dm_substitute_devices((*target)->params, len);
-
target = &((*target)->next);
}
- DMDEBUG("parsed %d targets", dm_setup_args.target_count);
+ DMDEBUG("parsed %d targets", dev->target_count);
- return 0;
+ return opt.next;
parse_fail:
- return 1;
+ return NULL;
+}
+
+static struct dm_device * __init dm_parse_args(void)
+{
+ struct dm_device *devices = NULL;
+ struct dm_device **tail = &devices;
+ struct dm_device *dev;
+ char *str = dm_setup_args.str;
+ unsigned long num_devices = dm_setup_args.num_devices;
+ unsigned long i;
+
+ if (!str)
+ return NULL;
+ for (i = 0; i < num_devices; i++) {
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ DMERR("failed to allocated memory for dev");
+ goto error;
+ }
+ *tail = dev;
+ tail = &dev->next;
+ /*
+ * devices are given minor numbers 0 - n-1
+ * in the order they are found in the arg
+ * string.
+ */
+ dev->minor = i;
+ str = dm_parse_device(dev, str);
+ if (!str) /* NULL indicates error in parsing, bail */
+ goto error;
+
+ str = dm_parse_targets(dev, str);
+ if (!str)
+ goto error;
+ }
+ return devices;
+error:
+ dm_setup_cleanup(devices);
+ return NULL;
}
/*
* Parse the command-line parameters given our kernel, but do not
* actually try to invoke the DM device now; that is handled by
- * dm_setup_drive after the low-level disk drivers have initialised.
- * dm format is as follows:
- * dm="name uuid fmode,[table line 1],[table line 2],..."
- * May be used with root=/dev/dm-0 as it always uses the first dm minor.
+ * dm_setup_drives after the low-level disk drivers have initialised.
+ * dm format is described at the top of the file.
+ *
+ * Because dm minor numbers are assigned in assending order starting with 0,
+ * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1,
+ * and so forth.
*/
-
static int __init dm_setup(char *str)
{
- dm_setup_args_init();
+ struct dm_option opt;
+ unsigned long num_devices;
- str = dm_setup_parse_device_args(str);
if (!str) {
DMDEBUG("str is NULL");
goto parse_fail;
}
-
- /* Target parsing is delayed until we have dynamic memory */
- dm_setup_args.targets = str;
-
- printk(KERN_INFO "dm: will configure '%s' on dm-%d\n",
- dm_setup_args.name, dm_setup_args.minor);
-
+ opt.next = str;
+ if (!get_dm_option(&opt, DM_FIELD_SEP))
+ goto parse_fail;
+ if (isdigit(opt.start[0])) { /* XXX: Optional number field */
+ num_devices = simple_strtoul(opt.start, NULL, 10);
+ str = opt.next;
+ } else {
+ num_devices = 1;
+ /* Don't advance str */
+ }
+ if (num_devices > DM_MAX_DEVICES) {
+ DMDEBUG("too many devices %lu > %d",
+ num_devices, DM_MAX_DEVICES);
+ }
+ dm_setup_args.str = str;
+ dm_setup_args.num_devices = num_devices;
+ DMINFO("will configure %lu devices", num_devices);
dm_early_setup = 1;
return 1;
parse_fail:
- printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n");
+ DMWARN("Invalid arguments supplied to dm=.");
return 0;
}
-
-static void __init dm_setup_drive(void)
+static void __init dm_setup_drives(void)
{
struct mapped_device *md = NULL;
struct dm_table *table = NULL;
struct dm_setup_target *target;
- char *uuid = dm_setup_args.uuid;
+ struct dm_device *dev;
+ char *uuid;
fmode_t fmode = FMODE_READ;
+ struct dm_device *devices;
- /* Finish parsing the targets. */
- if (dm_setup_parse_targets(dm_setup_args.targets))
- goto parse_fail;
-
- if (dm_create(dm_setup_args.minor, &md)) {
- DMDEBUG("failed to create the device");
- goto dm_create_fail;
- }
- DMDEBUG("created device '%s'", dm_device_name(md));
-
- /* In addition to flagging the table below, the disk must be
- * set explicitly ro/rw. */
- set_disk_ro(dm_disk(md), dm_setup_args.ro);
+ devices = dm_parse_args();
- if (!dm_setup_args.ro)
- fmode |= FMODE_WRITE;
- if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) {
- DMDEBUG("failed to create the table");
- goto dm_table_create_fail;
- }
+ for (dev = devices; dev; dev = dev->next) {
+ if (dm_create(dev->minor, &md)) {
+ DMDEBUG("failed to create the device");
+ goto dm_create_fail;
+ }
+ DMDEBUG("created device '%s'", dm_device_name(md));
+
+ /*
+ * In addition to flagging the table below, the disk must be
+ * set explicitly ro/rw.
+ */
+ set_disk_ro(dm_disk(md), dev->ro);
+
+ if (!dev->ro)
+ fmode |= FMODE_WRITE;
+ if (dm_table_create(&table, fmode, dev->target_count, md)) {
+ DMDEBUG("failed to create the table");
+ goto dm_table_create_fail;
+ }
- dm_lock_md_type(md);
- target = dm_setup_args.target;
- while (target) {
- DMINFO("adding target '%llu %llu %s %s'",
- (unsigned long long) target->begin,
- (unsigned long long) target->length, target->type,
- target->params);
- if (dm_table_add_target(table, target->type, target->begin,
- target->length, target->params)) {
- DMDEBUG("failed to add the target to the table");
- goto add_target_fail;
+ dm_lock_md_type(md);
+
+ for (target = dev->target; target; target = target->next) {
+ DMINFO("adding target '%llu %llu %s %s'",
+ (unsigned long long) target->begin,
+ (unsigned long long) target->length,
+ target->type, target->params);
+ if (dm_table_add_target(table, target->type,
+ target->begin,
+ target->length,
+ target->params)) {
+ DMDEBUG("failed to add the target"
+ " to the table");
+ goto add_target_fail;
+ }
+ }
+ if (dm_table_complete(table)) {
+ DMDEBUG("failed to complete the table");
+ goto table_complete_fail;
}
- target = target->next;
- }
- if (dm_table_complete(table)) {
- DMDEBUG("failed to complete the table");
- goto table_complete_fail;
- }
+ /* Suspend the device so that we can bind it to the table. */
+ if (dm_suspend(md, 0)) {
+ DMDEBUG("failed to suspend the device pre-bind");
+ goto suspend_fail;
+ }
- if (dm_get_md_type(md) == DM_TYPE_NONE) {
+ /* Initial table load: acquire type of table. */
dm_set_md_type(md, dm_table_get_type(table));
+
+ /* Setup md->queue to reflect md's type. */
if (dm_setup_md_queue(md)) {
DMWARN("unable to set up device queue for new table.");
goto setup_md_queue_fail;
}
- } else if (dm_get_md_type(md) != dm_table_get_type(table)) {
- DMWARN("can't change device type after initial table load.");
- goto setup_md_queue_fail;
- }
-
- /* Suspend the device so that we can bind it to the table. */
- if (dm_suspend(md, 0)) {
- DMDEBUG("failed to suspend the device pre-bind");
- goto suspend_fail;
- }
- /* Bind the table to the device. This is the only way to associate
- * md->map with the table and set the disk capacity directly. */
- if (dm_swap_table(md, table)) { /* should return NULL. */
- DMDEBUG("failed to bind the device to the table");
- goto table_bind_fail;
- }
+ /*
+ * Bind the table to the device. This is the only way
+ * to associate md->map with the table and set the disk
+ * capacity directly.
+ */
+ if (dm_swap_table(md, table)) { /* should return NULL. */
+ DMDEBUG("failed to bind the device to the table");
+ goto table_bind_fail;
+ }
- /* Finally, resume and the device should be ready. */
- if (dm_resume(md)) {
- DMDEBUG("failed to resume the device");
- goto resume_fail;
- }
+ /* Finally, resume and the device should be ready. */
+ if (dm_resume(md)) {
+ DMDEBUG("failed to resume the device");
+ goto resume_fail;
+ }
- /* Export the dm device via the ioctl interface */
- if (!strcmp(DM_NO_UUID, dm_setup_args.uuid))
- uuid = NULL;
- if (dm_ioctl_export(md, dm_setup_args.name, uuid)) {
- DMDEBUG("failed to export device with given name and uuid");
- goto export_fail;
- }
- printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor);
+ /* Export the dm device via the ioctl interface */
+ if (!strcmp(DM_NO_UUID, dev->uuid))
+ uuid = NULL;
+ if (dm_ioctl_export(md, dev->name, uuid)) {
+ DMDEBUG("failed to export device with given"
+ " name and uuid");
+ goto export_fail;
+ }
- dm_unlock_md_type(md);
- dm_setup_cleanup();
+ dm_unlock_md_type(md);
+
+ DMINFO("dm-%d is ready", dev->minor);
+ }
+ dm_setup_cleanup(devices);
return;
export_fail:
resume_fail:
table_bind_fail:
-suspend_fail:
setup_md_queue_fail:
+suspend_fail:
table_complete_fail:
add_target_fail:
dm_unlock_md_type(md);
dm_table_create_fail:
dm_put(md);
dm_create_fail:
- dm_setup_cleanup();
-parse_fail:
- printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n",
- dm_setup_args.minor, dm_setup_args.name);
+ DMWARN("starting dm-%d (%s) failed",
+ dev->minor, dev->name);
+ dm_setup_cleanup(devices);
}
__setup("dm=", dm_setup);
@@ -421,6 +465,6 @@ void __init dm_run_setup(void)
{
if (!dm_early_setup)
return;
- printk(KERN_INFO "dm: attempting early device configuration.\n");
- dm_setup_drive();
+ DMINFO("attempting early device configuration.");
+ dm_setup_drives();
}
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 0ca3599cee1f..e822cb0e18d5 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -593,8 +593,9 @@ out:
if (!switch_err) {
switch_err = switch_to_fair_policy();
- pr_err("Hotplug policy switch err. Task %s pid=%d\n",
- current->comm, current->pid);
+ if (switch_err)
+ pr_err("Hotplug policy switch err=%d Task %s pid=%d\n",
+ switch_err, current->comm, current->pid);
}
return err;
diff --git a/kernel/events/core.c b/kernel/events/core.c
index f9c6f554460e..06db8b64129d 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -9455,7 +9455,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn)
ret = inherit_task_group(event, parent, parent_ctx,
child, ctxn, &inherited_all);
if (ret)
- break;
+ goto out_unlock;
}
/*
@@ -9471,7 +9471,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn)
ret = inherit_task_group(event, parent, parent_ctx,
child, ctxn, &inherited_all);
if (ret)
- break;
+ goto out_unlock;
}
raw_spin_lock_irqsave(&parent_ctx->lock, flags);
@@ -9499,6 +9499,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn)
}
raw_spin_unlock_irqrestore(&parent_ctx->lock, flags);
+out_unlock:
mutex_unlock(&parent_ctx->mutex);
perf_unpin_context(parent_ctx);
diff --git a/kernel/fork.c b/kernel/fork.c
index 75573eeb49b2..622571d2a833 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -333,13 +333,14 @@ void set_task_stack_end_magic(struct task_struct *tsk)
*stackend = STACK_END_MAGIC; /* for overflow detection */
}
-static struct task_struct *dup_task_struct(struct task_struct *orig)
+static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
{
struct task_struct *tsk;
struct thread_info *ti;
- int node = tsk_fork_get_node(orig);
int err;
+ if (node == NUMA_NO_NODE)
+ node = tsk_fork_get_node(orig);
tsk = alloc_task_struct_node(node);
if (!tsk)
return NULL;
@@ -1276,7 +1277,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
int __user *child_tidptr,
struct pid *pid,
int trace,
- unsigned long tls)
+ unsigned long tls,
+ int node)
{
int retval;
struct task_struct *p;
@@ -1329,7 +1331,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
goto fork_out;
retval = -ENOMEM;
- p = dup_task_struct(current);
+ p = dup_task_struct(current, node);
if (!p)
goto fork_out;
@@ -1706,7 +1708,8 @@ static inline void init_idle_pids(struct pid_link *links)
struct task_struct *fork_idle(int cpu)
{
struct task_struct *task;
- task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0, 0);
+ task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0, 0,
+ cpu_to_node(cpu));
if (!IS_ERR(task)) {
init_idle_pids(task->pids);
init_idle(task, cpu, false);
@@ -1751,7 +1754,7 @@ long _do_fork(unsigned long clone_flags,
}
p = copy_process(clone_flags, stack_start, stack_size,
- child_tidptr, NULL, trace, tls);
+ child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
/*
* Do this prior waking up the new thread - the thread pointer
* might get invalid after that point, if the thread exits quickly.
diff --git a/kernel/futex.c b/kernel/futex.c
index beb042dcc332..af29863f3349 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -2690,7 +2690,6 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
{
struct hrtimer_sleeper timeout, *to = NULL;
struct rt_mutex_waiter rt_waiter;
- struct rt_mutex *pi_mutex = NULL;
struct futex_hash_bucket *hb;
union futex_key key2 = FUTEX_KEY_INIT;
struct futex_q q = futex_q_init;
@@ -2774,6 +2773,8 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
if (q.pi_state && (q.pi_state->owner != current)) {
spin_lock(q.lock_ptr);
ret = fixup_pi_state_owner(uaddr2, &q, current);
+ if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current)
+ rt_mutex_unlock(&q.pi_state->pi_mutex);
/*
* Drop the reference to the pi state which
* the requeue_pi() code acquired for us.
@@ -2782,6 +2783,8 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
spin_unlock(q.lock_ptr);
}
} else {
+ struct rt_mutex *pi_mutex;
+
/*
* We have been woken up by futex_unlock_pi(), a timeout, or a
* signal. futex_unlock_pi() will not destroy the lock_ptr nor
@@ -2805,18 +2808,19 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
if (res)
ret = (res < 0) ? res : 0;
+ /*
+ * If fixup_pi_state_owner() faulted and was unable to handle
+ * the fault, unlock the rt_mutex and return the fault to
+ * userspace.
+ */
+ if (ret && rt_mutex_owner(pi_mutex) == current)
+ rt_mutex_unlock(pi_mutex);
+
/* Unqueue and drop the lock. */
unqueue_me_pi(&q);
}
- /*
- * If fixup_pi_state_owner() faulted and was unable to handle the
- * fault, unlock the rt_mutex and return the fault to userspace.
- */
- if (ret == -EFAULT) {
- if (pi_mutex && rt_mutex_owner(pi_mutex) == current)
- rt_mutex_unlock(pi_mutex);
- } else if (ret == -EINTR) {
+ if (ret == -EINTR) {
/*
* We've already been requeued, but cannot restart by calling
* futex_lock_pi() directly. We could restart this syscall, but
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 3124cebaec31..2fc1177383a0 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -1159,6 +1159,22 @@ static int __init kaslr_nohibernate_setup(char *str)
return nohibernate_setup(str);
}
+static int __init page_poison_nohibernate_setup(char *str)
+{
+#ifdef CONFIG_PAGE_POISONING_ZERO
+ /*
+ * The zeroing option for page poison skips the checks on alloc.
+ * since hibernation doesn't save free pages there's no way to
+ * guarantee the pages will still be zeroed.
+ */
+ if (!strcmp(str, "on")) {
+ pr_info("Disabling hibernation due to page poisoning\n");
+ return nohibernate_setup(str);
+ }
+#endif
+ return 1;
+}
+
__setup("noresume", noresume_setup);
__setup("resume_offset=", resume_offset_setup);
__setup("resume=", resume_setup);
@@ -1167,3 +1183,4 @@ __setup("resumewait", resumewait_setup);
__setup("resumedelay=", resumedelay_setup);
__setup("nohibernate", nohibernate_setup);
__setup("kaslr", kaslr_nohibernate_setup);
+__setup("page_poison=", page_poison_nohibernate_setup);
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index 1dde338d30f2..0b5f2dea18a1 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -35,6 +35,7 @@ struct cluster_data {
unsigned int busy_down_thres[MAX_CPUS_PER_CLUSTER];
unsigned int active_cpus;
unsigned int num_cpus;
+ unsigned int nr_isolated_cpus;
cpumask_t cpu_mask;
unsigned int need_cpus;
unsigned int task_thres;
@@ -260,6 +261,7 @@ static ssize_t show_global_state(const struct cluster_data *state, char *buf)
ssize_t count = 0;
unsigned int cpu;
+ spin_lock_irq(&state_lock);
for_each_possible_cpu(cpu) {
c = &per_cpu(cpu_state, cpu);
cluster = c->cluster;
@@ -293,8 +295,12 @@ static ssize_t show_global_state(const struct cluster_data *state, char *buf)
count += snprintf(buf + count, PAGE_SIZE - count,
"\tNeed CPUs: %u\n", cluster->need_cpus);
count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tNr isolated CPUs: %u\n",
+ cluster->nr_isolated_cpus);
+ count += snprintf(buf + count, PAGE_SIZE - count,
"\tBoost: %u\n", (unsigned int) cluster->boost);
}
+ spin_unlock_irq(&state_lock);
return count;
}
@@ -525,7 +531,7 @@ static bool adjustment_possible(const struct cluster_data *cluster,
unsigned int need)
{
return (need < cluster->active_cpus || (need > cluster->active_cpus &&
- sched_isolate_count(&cluster->cpu_mask, false)));
+ cluster->nr_isolated_cpus));
}
static bool eval_need(struct cluster_data *cluster)
@@ -535,9 +541,8 @@ static bool eval_need(struct cluster_data *cluster)
unsigned int need_cpus = 0, last_need, thres_idx;
int ret = 0;
bool need_flag = false;
- unsigned int active_cpus;
unsigned int new_need;
- s64 now;
+ s64 now, elapsed;
if (unlikely(!cluster->inited))
return 0;
@@ -547,8 +552,8 @@ static bool eval_need(struct cluster_data *cluster)
if (cluster->boost) {
need_cpus = cluster->max_cpus;
} else {
- active_cpus = get_active_cpu_count(cluster);
- thres_idx = active_cpus ? active_cpus - 1 : 0;
+ cluster->active_cpus = get_active_cpu_count(cluster);
+ thres_idx = cluster->active_cpus ? cluster->active_cpus - 1 : 0;
list_for_each_entry(c, &cluster->lru, sib) {
if (c->busy >= cluster->busy_up_thres[thres_idx])
c->is_busy = true;
@@ -564,17 +569,16 @@ static bool eval_need(struct cluster_data *cluster)
last_need = cluster->need_cpus;
now = ktime_to_ms(ktime_get());
- if (new_need == last_need) {
- cluster->need_ts = now;
- spin_unlock_irqrestore(&state_lock, flags);
- return 0;
- }
-
- if (need_cpus > cluster->active_cpus) {
+ if (new_need > cluster->active_cpus) {
ret = 1;
- } else if (need_cpus < cluster->active_cpus) {
- s64 elapsed = now - cluster->need_ts;
+ } else {
+ if (new_need == last_need) {
+ cluster->need_ts = now;
+ spin_unlock_irqrestore(&state_lock, flags);
+ return 0;
+ }
+ elapsed = now - cluster->need_ts;
ret = elapsed >= cluster->offline_delay_ms;
}
@@ -582,7 +586,7 @@ static bool eval_need(struct cluster_data *cluster)
cluster->need_ts = now;
cluster->need_cpus = new_need;
}
- trace_core_ctl_eval_need(cluster->first_cpu, last_need, need_cpus,
+ trace_core_ctl_eval_need(cluster->first_cpu, last_need, new_need,
ret && need_flag);
spin_unlock_irqrestore(&state_lock, flags);
@@ -718,6 +722,7 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need)
struct cpu_data *c, *tmp;
unsigned long flags;
unsigned int num_cpus = cluster->num_cpus;
+ unsigned int nr_isolated = 0;
/*
* Protect against entry being removed (and added at tail) by other
@@ -742,12 +747,14 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need)
if (!sched_isolate_cpu(c->cpu)) {
c->isolated_by_us = true;
move_cpu_lru(c);
+ nr_isolated++;
} else {
pr_debug("Unable to isolate CPU%u\n", c->cpu);
}
cluster->active_cpus = get_active_cpu_count(cluster);
spin_lock_irqsave(&state_lock, flags);
}
+ cluster->nr_isolated_cpus += nr_isolated;
spin_unlock_irqrestore(&state_lock, flags);
/*
@@ -757,6 +764,7 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need)
if (cluster->active_cpus <= cluster->max_cpus)
return;
+ nr_isolated = 0;
num_cpus = cluster->num_cpus;
spin_lock_irqsave(&state_lock, flags);
list_for_each_entry_safe(c, tmp, &cluster->lru, sib) {
@@ -774,12 +782,14 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need)
if (!sched_isolate_cpu(c->cpu)) {
c->isolated_by_us = true;
move_cpu_lru(c);
+ nr_isolated++;
} else {
pr_debug("Unable to isolate CPU%u\n", c->cpu);
}
cluster->active_cpus = get_active_cpu_count(cluster);
spin_lock_irqsave(&state_lock, flags);
}
+ cluster->nr_isolated_cpus += nr_isolated;
spin_unlock_irqrestore(&state_lock, flags);
}
@@ -790,6 +800,7 @@ static void __try_to_unisolate(struct cluster_data *cluster,
struct cpu_data *c, *tmp;
unsigned long flags;
unsigned int num_cpus = cluster->num_cpus;
+ unsigned int nr_unisolated = 0;
/*
* Protect against entry being removed (and added at tail) by other
@@ -814,12 +825,14 @@ static void __try_to_unisolate(struct cluster_data *cluster,
if (!sched_unisolate_cpu(c->cpu)) {
c->isolated_by_us = false;
move_cpu_lru(c);
+ nr_unisolated++;
} else {
pr_debug("Unable to unisolate CPU%u\n", c->cpu);
}
cluster->active_cpus = get_active_cpu_count(cluster);
spin_lock_irqsave(&state_lock, flags);
}
+ cluster->nr_isolated_cpus -= nr_unisolated;
spin_unlock_irqrestore(&state_lock, flags);
}
@@ -885,10 +898,11 @@ static int __ref cpu_callback(struct notifier_block *nfb,
struct cpu_data *state = &per_cpu(cpu_state, cpu);
struct cluster_data *cluster = state->cluster;
unsigned int need;
- int ret = NOTIFY_OK;
+ bool do_wakeup, unisolated = false;
+ unsigned long flags;
if (unlikely(!cluster || !cluster->inited))
- return NOTIFY_OK;
+ return NOTIFY_DONE;
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
@@ -911,6 +925,7 @@ static int __ref cpu_callback(struct notifier_block *nfb,
if (state->isolated_by_us) {
sched_unisolate_cpu_unlocked(cpu);
state->isolated_by_us = false;
+ unisolated = true;
}
/* Move a CPU to the end of the LRU when it goes offline. */
@@ -919,13 +934,20 @@ static int __ref cpu_callback(struct notifier_block *nfb,
state->busy = 0;
cluster->active_cpus = get_active_cpu_count(cluster);
break;
+ default:
+ return NOTIFY_DONE;
}
need = apply_limits(cluster, cluster->need_cpus);
- if (adjustment_possible(cluster, need))
+ spin_lock_irqsave(&state_lock, flags);
+ if (unisolated)
+ cluster->nr_isolated_cpus--;
+ do_wakeup = adjustment_possible(cluster, need);
+ spin_unlock_irqrestore(&state_lock, flags);
+ if (do_wakeup)
wake_up_core_ctl_thread(cluster);
- return ret;
+ return NOTIFY_OK;
}
static struct notifier_block __refdata cpu_notifier = {
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 685ae83b2bfa..3d55ec89c400 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1843,12 +1843,11 @@ static void switched_to_dl(struct rq *rq, struct task_struct *p)
#ifdef CONFIG_SMP
if (p->nr_cpus_allowed > 1 && rq->dl.overloaded)
queue_push_tasks(rq);
-#else
+#endif
if (dl_task(rq->curr))
check_preempt_curr_dl(rq, p, 0);
else
resched_curr(rq);
-#endif
}
}
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 8d5353906c8d..4d96380b35e8 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6060,7 +6060,7 @@ long group_norm_util(struct energy_env *eenv, struct sched_group *sg)
}
static int find_new_capacity(struct energy_env *eenv,
- const struct sched_group_energy const *sge)
+ const struct sched_group_energy * const sge)
{
int idx;
unsigned long util = group_max_util(eenv);
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 07b2c63e4983..29345ed74069 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -2425,10 +2425,9 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p)
#ifdef CONFIG_SMP
if (p->nr_cpus_allowed > 1 && rq->rt.overloaded)
queue_push_tasks(rq);
-#else
+#endif /* CONFIG_SMP */
if (p->prio < rq->curr->prio)
resched_curr(rq);
-#endif /* CONFIG_SMP */
}
}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 75500042fd32..276a2387f06f 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1011,7 +1011,7 @@ struct sched_group {
unsigned int group_weight;
struct sched_group_capacity *sgc;
- const struct sched_group_energy const *sge;
+ const struct sched_group_energy *sge;
/*
* The CPUs this group covers.
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 651ff1a3a306..ec2102104cb8 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -19,6 +19,7 @@
#include <linux/percpu.h>
#include <linux/profile.h>
#include <linux/sched.h>
+#include <linux/timer.h>
#include <linux/module.h>
#include <linux/irq_work.h>
#include <linux/posix-timers.h>
@@ -809,6 +810,11 @@ static void __tick_nohz_idle_enter(struct tick_sched *ts)
now = tick_nohz_start_idle(ts);
+#ifdef CONFIG_SMP
+ if (check_pending_deferrable_timers(cpu))
+ raise_softirq_irqoff(TIMER_SOFTIRQ);
+#endif
+
if (can_stop_idle_tick(cpu, ts)) {
int was_stopped = ts->tick_stopped;
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 2bde2c2b1cb3..8315d4d72cc3 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -102,6 +102,7 @@ static DEFINE_PER_CPU(struct tvec_base, tvec_bases);
unsigned int sysctl_timer_migration = 1;
struct tvec_base tvec_base_deferrable;
+static atomic_t deferrable_pending;
void timers_update_migration(bool update_nohz)
{
@@ -150,9 +151,12 @@ static inline struct tvec_base *get_target_base(struct tvec_base *base,
static inline void __run_deferrable_timers(void)
{
- if (smp_processor_id() == tick_do_timer_cpu &&
- time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies))
- __run_timers(&tvec_base_deferrable);
+ if (time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies)) {
+ if ((atomic_cmpxchg(&deferrable_pending, 1, 0) &&
+ tick_do_timer_cpu == TICK_DO_TIMER_NONE) ||
+ tick_do_timer_cpu == smp_processor_id())
+ __run_timers(&tvec_base_deferrable);
+ }
}
static inline void init_timer_deferrable_global(void)
@@ -1428,6 +1432,30 @@ static u64 cmp_next_hrtimer_event(u64 basem, u64 expires)
return DIV_ROUND_UP_ULL(nextevt, TICK_NSEC) * TICK_NSEC;
}
+#ifdef CONFIG_SMP
+/*
+ * check_pending_deferrable_timers - Check for unbound deferrable timer expiry.
+ * @cpu - Current CPU
+ *
+ * The function checks whether any global deferrable pending timers
+ * are exipired or not. This function does not check cpu bounded
+ * diferrable pending timers expiry.
+ *
+ * The function returns true when a cpu unbounded deferrable timer is expired.
+ */
+bool check_pending_deferrable_timers(int cpu)
+{
+ if (cpu == tick_do_timer_cpu ||
+ tick_do_timer_cpu == TICK_DO_TIMER_NONE) {
+ if (time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies)
+ && !atomic_cmpxchg(&deferrable_pending, 0, 1)) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
/**
* get_next_timer_interrupt - return the time (clock mono) of the next timer
* @basej: base time jiffies
diff --git a/kernel/trace/msm_rtb.c b/kernel/trace/msm_rtb.c
index 80058b544cb5..587082117842 100644
--- a/kernel/trace/msm_rtb.c
+++ b/kernel/trace/msm_rtb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -28,6 +28,7 @@
#include <asm-generic/sizes.h>
#include <linux/msm_rtb.h>
#include <asm/timex.h>
+#include <soc/qcom/minidump.h>
#define SENTINEL_BYTE_1 0xFF
#define SENTINEL_BYTE_2 0xAA
@@ -243,6 +244,7 @@ EXPORT_SYMBOL(uncached_logk);
static int msm_rtb_probe(struct platform_device *pdev)
{
struct msm_rtb_platform_data *d = pdev->dev.platform_data;
+ struct md_region md_entry;
#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
unsigned int cpu;
#endif
@@ -294,6 +296,12 @@ static int msm_rtb_probe(struct platform_device *pdev)
memset(msm_rtb.rtb, 0, msm_rtb.size);
+ strlcpy(md_entry.name, "KRTB_BUF", sizeof(md_entry.name));
+ md_entry.virt_addr = (uintptr_t)msm_rtb.rtb;
+ md_entry.phys_addr = msm_rtb.phys;
+ md_entry.size = msm_rtb.size;
+ if (msm_minidump_add_region(&md_entry))
+ pr_info("Failed to add RTB in Minidump\n");
#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
for_each_possible_cpu(cpu) {
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 1de2ef8ec926..f527d8e65d69 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -123,9 +123,7 @@ static unsigned long soft_lockup_nmi_warn;
#ifdef CONFIG_HARDLOCKUP_DETECTOR
unsigned int __read_mostly hardlockup_panic =
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE;
-#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
static unsigned long __maybe_unused hardlockup_allcpu_dumped;
-#endif
/*
* We may not want to enable hard lockup detection by default in all cases,
* for example when running the kernel as a guest on a hypervisor. In these
diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
index 9e55d6b141aa..7470fd60fc59 100644
--- a/mm/Kconfig.debug
+++ b/mm/Kconfig.debug
@@ -40,9 +40,6 @@ config DEBUG_PAGEALLOC_ENABLE_DEFAULT
Enable debug page memory allocations by default? This value
can be overridden by debug_pagealloc=off|on.
-config PAGE_POISONING
- bool
-
config SLUB_DEBUG_PANIC_ON
bool "Enable to Panic on SLUB corruption detection"
depends on SLUB_DEBUG
@@ -52,3 +49,53 @@ config SLUB_DEBUG_PANIC_ON
debug options this may not be desirable as it prevents from
investigating the root cause which may be rooted within cache
or memory.
+
+config PAGE_POISONING
+ bool "Poison pages after freeing"
+ select PAGE_EXTENSION
+ select PAGE_POISONING_NO_SANITY if HIBERNATION
+ ---help---
+ Fill the pages with poison patterns after free_pages() and verify
+ the patterns before alloc_pages. The filling of the memory helps
+ reduce the risk of information leaks from freed data. This does
+ have a potential performance impact.
+
+ Note that "poison" here is not the same thing as the "HWPoison"
+ for CONFIG_MEMORY_FAILURE. This is software poisoning only.
+
+ If unsure, say N
+
+config PAGE_POISONING_ENABLE_DEFAULT
+ bool "Enable page poisoning by default?"
+ default n
+ depends on PAGE_POISONING
+ ---help---
+ Enable page poisoning of free pages by default? This value
+ can be overridden by page_poison=off|on. This can be used
+ to avoid passing the kernel parameter and let page poisoning
+ feature enabled by default.
+
+config PAGE_POISONING_NO_SANITY
+ depends on PAGE_POISONING
+ bool "Only poison, don't sanity check"
+ ---help---
+ Skip the sanity checking on alloc, only fill the pages with
+ poison on free. This reduces some of the overhead of the
+ poisoning feature.
+
+ If you are only interested in sanitization, say Y. Otherwise
+ say N.
+
+config PAGE_POISONING_ZERO
+ bool "Use zero for poisoning instead of random data"
+ depends on PAGE_POISONING
+ ---help---
+ Instead of using the existing poison value, fill the pages with
+ zeros. This makes it harder to detect when errors are occurring
+ due to sanitization but the zeroing at free means that it is
+ no longer necessary to write zeros when GFP_ZERO is used on
+ allocation.
+
+ Enabling page poisoning with this option will disable hibernation
+
+ If unsure, say N
diff --git a/mm/Makefile b/mm/Makefile
index 4b1a69abce7a..130d06ac56e0 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -52,7 +52,7 @@ obj-$(CONFIG_SPARSEMEM_VMEMMAP) += sparse-vmemmap.o
obj-$(CONFIG_SLOB) += slob.o
obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o
obj-$(CONFIG_KSM) += ksm.o
-obj-$(CONFIG_PAGE_POISONING) += debug-pagealloc.o
+obj-$(CONFIG_PAGE_POISONING) += page_poison.o
obj-$(CONFIG_SLAB) += slab.o
obj-$(CONFIG_SLUB) += slub.o
obj-$(CONFIG_KMEMCHECK) += kmemcheck.o
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 28f60d9ea074..170c1486e5c9 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1012,6 +1012,7 @@ static bool free_pages_prepare(struct page *page, unsigned int order)
PAGE_SIZE << order);
}
arch_free_page(page, order);
+ kernel_poison_pages(page, 1 << order, 0);
kernel_map_pages(page, 1 << order, 0);
kasan_free_pages(page, order);
@@ -1396,6 +1397,12 @@ static inline int check_new_page(struct page *page)
return 0;
}
+static inline bool free_pages_prezeroed(void)
+{
+ return IS_ENABLED(CONFIG_PAGE_POISONING_ZERO) &&
+ page_poisoning_enabled();
+}
+
static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
int alloc_flags)
{
@@ -1413,8 +1420,9 @@ static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
kasan_alloc_pages(page, order);
arch_alloc_page(page, order);
kernel_map_pages(page, 1 << order, 1);
+ kernel_poison_pages(page, 1 << order, 1);
- if (gfp_flags & __GFP_ZERO)
+ if (!free_pages_prezeroed() && (gfp_flags & __GFP_ZERO))
for (i = 0; i < (1 << order); i++)
clear_highpage(page + i);
diff --git a/mm/page_ext.c b/mm/page_ext.c
index 292ca7b8debd..916accfec86a 100644
--- a/mm/page_ext.c
+++ b/mm/page_ext.c
@@ -54,9 +54,6 @@
static struct page_ext_operations *page_ext_ops[] = {
&debug_guardpage_ops,
-#ifdef CONFIG_PAGE_POISONING
- &page_poisoning_ops,
-#endif
#ifdef CONFIG_PAGE_OWNER
&page_owner_ops,
#endif
@@ -106,12 +103,15 @@ struct page_ext *lookup_page_ext(struct page *page)
struct page_ext *base;
base = NODE_DATA(page_to_nid(page))->node_page_ext;
-#ifdef CONFIG_DEBUG_VM
+#if defined(CONFIG_DEBUG_VM) || defined(CONFIG_PAGE_POISONING)
/*
* The sanity checks the page allocator does upon freeing a
* page can reach here before the page_ext arrays are
* allocated when feeding a range of pages to the allocator
* for the first time during bootup or memory hotplug.
+ *
+ * This check is also necessary for ensuring page poisoning
+ * works as expected when enabled
*/
if (unlikely(!base))
return NULL;
@@ -180,12 +180,15 @@ struct page_ext *lookup_page_ext(struct page *page)
{
unsigned long pfn = page_to_pfn(page);
struct mem_section *section = __pfn_to_section(pfn);
-#ifdef CONFIG_DEBUG_VM
+#if defined(CONFIG_DEBUG_VM) || defined(CONFIG_PAGE_POISONING)
/*
* The sanity checks the page allocator does upon freeing a
* page can reach here before the page_ext arrays are
* allocated when feeding a range of pages to the allocator
* for the first time during bootup or memory hotplug.
+ *
+ * This check is also necessary for ensuring page poisoning
+ * works as expected when enabled
*/
if (!section->page_ext)
return NULL;
diff --git a/mm/debug-pagealloc.c b/mm/page_poison.c
index 100963091cc6..c8cf230dbfcb 100644
--- a/mm/debug-pagealloc.c
+++ b/mm/page_poison.c
@@ -6,68 +6,41 @@
#include <linux/poison.h>
#include <linux/ratelimit.h>
-#ifndef mark_addr_rdonly
-#define mark_addr_rdonly(a)
-#endif
-
-#ifndef mark_addr_rdwrite
-#define mark_addr_rdwrite(a)
-#endif
-
-static bool page_poisoning_enabled __read_mostly;
-
-static bool need_page_poisoning(void)
-{
- if (!debug_pagealloc_enabled())
- return false;
-
- return true;
-}
-
-static void init_page_poisoning(void)
-{
- if (!debug_pagealloc_enabled())
- return;
-
- page_poisoning_enabled = true;
-}
-
-struct page_ext_operations page_poisoning_ops = {
- .need = need_page_poisoning,
- .init = init_page_poisoning,
-};
+static bool want_page_poisoning __read_mostly
+ = IS_ENABLED(CONFIG_PAGE_POISONING_ENABLE_DEFAULT);
-static inline void set_page_poison(struct page *page)
+static int early_page_poison_param(char *buf)
{
- struct page_ext *page_ext;
+ if (!buf)
+ return -EINVAL;
- page_ext = lookup_page_ext(page);
- __set_bit(PAGE_EXT_DEBUG_POISON, &page_ext->flags);
-}
-
-static inline void clear_page_poison(struct page *page)
-{
- struct page_ext *page_ext;
+ if (strcmp(buf, "on") == 0)
+ want_page_poisoning = true;
+ else if (strcmp(buf, "off") == 0)
+ want_page_poisoning = false;
- page_ext = lookup_page_ext(page);
- __clear_bit(PAGE_EXT_DEBUG_POISON, &page_ext->flags);
+ return 0;
}
+early_param("page_poison", early_page_poison_param);
-static inline bool page_poison(struct page *page)
+bool page_poisoning_enabled(void)
{
- struct page_ext *page_ext;
-
- page_ext = lookup_page_ext(page);
- return test_bit(PAGE_EXT_DEBUG_POISON, &page_ext->flags);
+ /*
+ * Assumes that debug_pagealloc_enabled is set before
+ * free_all_bootmem.
+ * Page poisoning is debug page alloc for some arches. If
+ * either of those options are enabled, enable poisoning.
+ */
+ return (want_page_poisoning ||
+ (!IS_ENABLED(CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC) &&
+ debug_pagealloc_enabled()));
}
static void poison_page(struct page *page)
{
void *addr = kmap_atomic(page);
- set_page_poison(page);
memset(addr, PAGE_POISON, PAGE_SIZE);
- mark_addr_rdonly(addr);
kunmap_atomic(addr);
}
@@ -93,6 +66,9 @@ static void check_poison_mem(struct page *page,
unsigned char *start;
unsigned char *end;
+ if (IS_ENABLED(CONFIG_PAGE_POISONING_NO_SANITY))
+ return;
+
start = memchr_inv(mem, PAGE_POISON, bytes);
if (!start)
return;
@@ -121,13 +97,13 @@ static void unpoison_page(struct page *page)
{
void *addr;
- if (!page_poison(page))
- return;
-
addr = kmap_atomic(page);
+ /*
+ * Page poisoning when enabled poisons each and every page
+ * that is freed to buddy. Thus no extra check is done to
+ * see if a page was posioned.
+ */
check_poison_mem(page, addr, PAGE_SIZE);
- mark_addr_rdwrite(addr);
- clear_page_poison(page);
kunmap_atomic(addr);
}
@@ -139,9 +115,9 @@ static void unpoison_pages(struct page *page, int n)
unpoison_page(page + i);
}
-void __kernel_map_pages(struct page *page, int numpages, int enable)
+void kernel_poison_pages(struct page *page, int numpages, int enable)
{
- if (!page_poisoning_enabled)
+ if (!page_poisoning_enabled())
return;
if (enable)
@@ -149,3 +125,10 @@ void __kernel_map_pages(struct page *page, int numpages, int enable)
else
poison_pages(page, numpages);
}
+
+#ifndef CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC
+void __kernel_map_pages(struct page *page, int numpages, int enable)
+{
+ /* This function does nothing, all work is done via poison pages */
+}
+#endif
diff --git a/mm/percpu.c b/mm/percpu.c
index 1f376bce413c..ef6353f0adbd 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -1012,8 +1012,11 @@ area_found:
mutex_unlock(&pcpu_alloc_mutex);
}
- if (chunk != pcpu_reserved_chunk)
+ if (chunk != pcpu_reserved_chunk) {
+ spin_lock_irqsave(&pcpu_lock, flags);
pcpu_nr_empty_pop_pages -= occ_pages;
+ spin_unlock_irqrestore(&pcpu_lock, flags);
+ }
if (pcpu_nr_empty_pop_pages < PCPU_EMPTY_POP_PAGES_LOW)
pcpu_schedule_balance_work();
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 5e9e74955bd1..94fecacf0ddc 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -399,6 +399,35 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl,
return freed;
}
+static void shrink_slab_lmk(gfp_t gfp_mask, int nid,
+ struct mem_cgroup *memcg,
+ unsigned long nr_scanned,
+ unsigned long nr_eligible)
+{
+ struct shrinker *shrinker;
+
+ if (nr_scanned == 0)
+ nr_scanned = SWAP_CLUSTER_MAX;
+
+ if (!down_read_trylock(&shrinker_rwsem))
+ goto out;
+
+ list_for_each_entry(shrinker, &shrinker_list, list) {
+ struct shrink_control sc = {
+ .gfp_mask = gfp_mask,
+ };
+
+ if (!(shrinker->flags & SHRINKER_LMK))
+ continue;
+
+ do_shrink_slab(&sc, shrinker, nr_scanned, nr_eligible);
+ }
+
+ up_read(&shrinker_rwsem);
+out:
+ cond_resched();
+}
+
/**
* shrink_slab - shrink slab caches
* @gfp_mask: allocation context
@@ -460,6 +489,9 @@ static unsigned long shrink_slab(gfp_t gfp_mask, int nid,
.memcg = memcg,
};
+ if (shrinker->flags & SHRINKER_LMK)
+ continue;
+
if (memcg && !(shrinker->flags & SHRINKER_MEMCG_AWARE))
continue;
@@ -2626,6 +2658,7 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
gfp_t orig_mask;
enum zone_type requested_highidx = gfp_zone(sc->gfp_mask);
bool reclaimable = false;
+ unsigned long lru_pages = 0;
/*
* If the number of buffer_heads in the machine exceeds the maximum
@@ -2653,6 +2686,7 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
* to global LRU.
*/
if (global_reclaim(sc)) {
+ lru_pages += zone_reclaimable_pages(zone);
if (!cpuset_zone_allowed(zone,
GFP_KERNEL | __GFP_HARDWALL))
continue;
@@ -2703,6 +2737,9 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
reclaimable = true;
}
+ if (global_reclaim(sc))
+ shrink_slab_lmk(sc->gfp_mask, 0, NULL,
+ sc->nr_scanned, lru_pages);
/*
* Restore to original mask to avoid the impact on the caller if we
* promoted it to __GFP_HIGHMEM.
@@ -3181,7 +3218,8 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining,
*/
static bool kswapd_shrink_zone(struct zone *zone,
int classzone_idx,
- struct scan_control *sc)
+ struct scan_control *sc,
+ unsigned long lru_pages)
{
unsigned long balance_gap;
bool lowmem_pressure;
@@ -3208,6 +3246,8 @@ static bool kswapd_shrink_zone(struct zone *zone,
return true;
shrink_zone(zone, sc, zone_idx(zone) == classzone_idx);
+ shrink_slab_lmk(sc->gfp_mask, zone_to_nid(zone), NULL,
+ sc->nr_scanned, lru_pages);
clear_bit(ZONE_WRITEBACK, &zone->flags);
@@ -3265,6 +3305,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx)
do {
bool raise_priority = true;
+ unsigned long lru_pages = 0;
sc.nr_reclaimed = 0;
@@ -3322,6 +3363,15 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx)
if (sc.priority < DEF_PRIORITY - 2)
sc.may_writepage = 1;
+ for (i = 0; i <= end_zone; i++) {
+ struct zone *zone = pgdat->node_zones + i;
+
+ if (!populated_zone(zone))
+ continue;
+
+ lru_pages += zone_reclaimable_pages(zone);
+ }
+
/*
* Now scan the zone in the dma->highmem direction, stopping
* at the last zone which needs scanning.
@@ -3358,7 +3408,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx)
* that that high watermark would be met at 100%
* efficiency.
*/
- if (kswapd_shrink_zone(zone, end_zone, &sc))
+ if (kswapd_shrink_zone(zone, end_zone, &sc, lru_pages))
raise_priority = false;
}
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index f7fba74108a9..e24754a0e052 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -29,6 +29,7 @@ EXPORT_SYMBOL(br_should_route_hook);
static int
br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
{
+ br_drop_fake_rtable(skb);
return netif_receive_skb(skb);
}
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 7ddbe7ec81d6..97fc19f001bf 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -516,21 +516,6 @@ static unsigned int br_nf_pre_routing(void *priv,
}
-/* PF_BRIDGE/LOCAL_IN ************************************************/
-/* The packet is locally destined, which requires a real
- * dst_entry, so detach the fake one. On the way up, the
- * packet would pass through PRE_ROUTING again (which already
- * took place when the packet entered the bridge), but we
- * register an IPv4 PRE_ROUTING 'sabotage' hook that will
- * prevent this from happening. */
-static unsigned int br_nf_local_in(void *priv,
- struct sk_buff *skb,
- const struct nf_hook_state *state)
-{
- br_drop_fake_rtable(skb);
- return NF_ACCEPT;
-}
-
/* PF_BRIDGE/FORWARD *************************************************/
static int br_nf_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
@@ -901,12 +886,6 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = {
.priority = NF_BR_PRI_BRNF,
},
{
- .hook = br_nf_local_in,
- .pf = NFPROTO_BRIDGE,
- .hooknum = NF_BR_LOCAL_IN,
- .priority = NF_BR_PRI_BRNF,
- },
- {
.hook = br_nf_forward_ip,
.pf = NFPROTO_BRIDGE,
.hooknum = NF_BR_FORWARD,
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index ddc3573894b0..bc95e48d5cfb 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -1265,7 +1265,6 @@ static int decode_new_up_state_weight(void **p, void *end,
if ((map->osd_state[osd] & CEPH_OSD_EXISTS) &&
(xorstate & CEPH_OSD_EXISTS)) {
pr_info("osd%d does not exist\n", osd);
- map->osd_weight[osd] = CEPH_OSD_IN;
ret = set_primary_affinity(map, osd,
CEPH_OSD_DEFAULT_PRIMARY_AFFINITY);
if (ret)
diff --git a/net/core/dev.c b/net/core/dev.c
index 51aed87e8eec..2587d7f30191 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1679,27 +1679,54 @@ EXPORT_SYMBOL_GPL(net_dec_ingress_queue);
static struct static_key netstamp_needed __read_mostly;
#ifdef HAVE_JUMP_LABEL
static atomic_t netstamp_needed_deferred;
+static atomic_t netstamp_wanted;
static void netstamp_clear(struct work_struct *work)
{
int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
+ int wanted;
- while (deferred--)
- static_key_slow_dec(&netstamp_needed);
+ wanted = atomic_add_return(deferred, &netstamp_wanted);
+ if (wanted > 0)
+ static_key_enable(&netstamp_needed);
+ else
+ static_key_disable(&netstamp_needed);
}
static DECLARE_WORK(netstamp_work, netstamp_clear);
#endif
void net_enable_timestamp(void)
{
+#ifdef HAVE_JUMP_LABEL
+ int wanted;
+
+ while (1) {
+ wanted = atomic_read(&netstamp_wanted);
+ if (wanted <= 0)
+ break;
+ if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted + 1) == wanted)
+ return;
+ }
+ atomic_inc(&netstamp_needed_deferred);
+ schedule_work(&netstamp_work);
+#else
static_key_slow_inc(&netstamp_needed);
+#endif
}
EXPORT_SYMBOL(net_enable_timestamp);
void net_disable_timestamp(void)
{
#ifdef HAVE_JUMP_LABEL
- /* net_disable_timestamp() can be called from non process context */
- atomic_inc(&netstamp_needed_deferred);
+ int wanted;
+
+ while (1) {
+ wanted = atomic_read(&netstamp_wanted);
+ if (wanted <= 1)
+ break;
+ if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted - 1) == wanted)
+ return;
+ }
+ atomic_dec(&netstamp_needed_deferred);
schedule_work(&netstamp_work);
#else
static_key_slow_dec(&netstamp_needed);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 5dd643d524d6..fedcee8263b6 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3704,13 +3704,14 @@ void skb_complete_tx_timestamp(struct sk_buff *skb,
if (!skb_may_tx_timestamp(sk, false))
return;
- /* take a reference to prevent skb_orphan() from freeing the socket */
- sock_hold(sk);
-
- *skb_hwtstamps(skb) = *hwtstamps;
- __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND);
-
- sock_put(sk);
+ /* Take a reference to prevent skb_orphan() from freeing the socket,
+ * but only if the socket refcount is not zero.
+ */
+ if (likely(atomic_inc_not_zero(&sk->sk_refcnt))) {
+ *skb_hwtstamps(skb) = *hwtstamps;
+ __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND);
+ sock_put(sk);
+ }
}
EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
@@ -3761,7 +3762,7 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
{
struct sock *sk = skb->sk;
struct sock_exterr_skb *serr;
- int err;
+ int err = 1;
skb->wifi_acked_valid = 1;
skb->wifi_acked = acked;
@@ -3771,14 +3772,15 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
serr->ee.ee_errno = ENOMSG;
serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
- /* take a reference to prevent skb_orphan() from freeing the socket */
- sock_hold(sk);
-
- err = sock_queue_err_skb(sk, skb);
+ /* Take a reference to prevent skb_orphan() from freeing the socket,
+ * but only if the socket refcount is not zero.
+ */
+ if (likely(atomic_inc_not_zero(&sk->sk_refcnt))) {
+ err = sock_queue_err_skb(sk, skb);
+ sock_put(sk);
+ }
if (err)
kfree_skb(skb);
-
- sock_put(sk);
}
EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
diff --git a/net/core/sock.c b/net/core/sock.c
index a84a154cdf0c..4efaa3b6633d 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1463,6 +1463,11 @@ static void __sk_destruct(struct rcu_head *head)
pr_debug("%s: optmem leakage (%d bytes) detected\n",
__func__, atomic_read(&sk->sk_omem_alloc));
+ if (sk->sk_frag.page) {
+ put_page(sk->sk_frag.page);
+ sk->sk_frag.page = NULL;
+ }
+
if (sk->sk_peer_cred)
put_cred(sk->sk_peer_cred);
put_pid(sk->sk_peer_pid);
@@ -1564,6 +1569,12 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
is_charged = sk_filter_charge(newsk, filter);
if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
+ /* We need to make sure that we don't uncharge the new
+ * socket if we couldn't charge it in the first place
+ * as otherwise we uncharge the parent's filter.
+ */
+ if (!is_charged)
+ RCU_INIT_POINTER(newsk->sk_filter, NULL);
/* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free() */
newsk->sk_destruct = NULL;
@@ -2706,11 +2717,6 @@ void sk_common_release(struct sock *sk)
sk_refcnt_debug_release(sk);
- if (sk->sk_frag.page) {
- put_page(sk->sk_frag.page);
- sk->sk_frag.page = NULL;
- }
-
sock_put(sk);
}
EXPORT_SYMBOL(sk_common_release);
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index f053198e730c..5e3a7302f774 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -749,6 +749,7 @@ static void ccid2_hc_tx_exit(struct sock *sk)
for (i = 0; i < hc->tx_seqbufc; i++)
kfree(hc->tx_seqbuf[i]);
hc->tx_seqbufc = 0;
+ dccp_ackvec_parsed_cleanup(&hc->tx_av_chunks);
}
static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 0759f5b9180e..6467bf392e1b 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -289,7 +289,8 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
switch (type) {
case ICMP_REDIRECT:
- dccp_do_redirect(skb, sk);
+ if (!sock_owned_by_user(sk))
+ dccp_do_redirect(skb, sk);
goto out;
case ICMP_SOURCE_QUENCH:
/* Just silently ignore these. */
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 27c4e81efa24..8113ad58fcb4 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -122,10 +122,12 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
np = inet6_sk(sk);
if (type == NDISC_REDIRECT) {
- struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
+ if (!sock_owned_by_user(sk)) {
+ struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
- if (dst)
- dst->ops->redirect(dst, sk, skb);
+ if (dst)
+ dst->ops->redirect(dst, sk, skb);
+ }
goto out;
}
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 1994f8af646b..68eed344b471 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -122,6 +122,7 @@ struct sock *dccp_create_openreq_child(const struct sock *sk,
/* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free() */
newsk->sk_destruct = NULL;
+ bh_unlock_sock(newsk);
sk_free(newsk);
return NULL;
}
@@ -145,6 +146,13 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
struct dccp_request_sock *dreq = dccp_rsk(req);
bool own_req;
+ /* TCP/DCCP listeners became lockless.
+ * DCCP stores complex state in its request_sock, so we need
+ * a protection for them, now this code runs without being protected
+ * by the parent (listener) lock.
+ */
+ spin_lock_bh(&dreq->dreq_lock);
+
/* Check for retransmitted REQUEST */
if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) {
@@ -159,7 +167,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
inet_rtx_syn_ack(sk, req);
}
/* Network Duplicate, discard packet */
- return NULL;
+ goto out;
}
DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR;
@@ -185,20 +193,20 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
req, &own_req);
- if (!child)
- goto listen_overflow;
-
- return inet_csk_complete_hashdance(sk, child, req, own_req);
+ if (child) {
+ child = inet_csk_complete_hashdance(sk, child, req, own_req);
+ goto out;
+ }
-listen_overflow:
- dccp_pr_debug("listen_overflow!\n");
DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY;
drop:
if (dccp_hdr(skb)->dccph_type != DCCP_PKT_RESET)
req->rsk_ops->send_reset(sk, skb);
inet_csk_reqsk_queue_drop(sk, req);
- return NULL;
+out:
+ spin_unlock_bh(&dreq->dreq_lock);
+ return child;
}
EXPORT_SYMBOL_GPL(dccp_check_req);
@@ -249,6 +257,7 @@ int dccp_reqsk_init(struct request_sock *req,
{
struct dccp_request_sock *dreq = dccp_rsk(req);
+ spin_lock_init(&dreq->dreq_lock);
inet_rsk(req)->ir_rmt_port = dccp_hdr(skb)->dccph_sport;
inet_rsk(req)->ir_num = ntohs(dccp_hdr(skb)->dccph_dport);
inet_rsk(req)->acked = 0;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 7e30c7b50a28..a353d1d92f01 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -1081,7 +1081,8 @@ static void nl_fib_input(struct sk_buff *skb)
net = sock_net(skb->sk);
nlh = nlmsg_hdr(skb);
- if (skb->len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len ||
+ if (skb->len < nlmsg_total_size(sizeof(*frn)) ||
+ skb->len < nlh->nlmsg_len ||
nlmsg_len(nlh) < sizeof(*frn))
return;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 6ace04d14e30..9af0f461b00d 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1963,6 +1963,7 @@ int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
{
int res;
+ tos &= IPTOS_RT_MASK;
rcu_read_lock();
/* Multicast recognition logic is moved from route cache to here.
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index e7e227f6760f..3f87c731477f 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5437,6 +5437,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)
struct inet_connection_sock *icsk = inet_csk(sk);
tcp_set_state(sk, TCP_ESTABLISHED);
+ icsk->icsk_ack.lrcvtime = tcp_time_stamp;
if (skb) {
icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);
@@ -5649,7 +5650,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
* to stand against the temptation 8) --ANK
*/
inet_csk_schedule_ack(sk);
- icsk->icsk_ack.lrcvtime = tcp_time_stamp;
tcp_enter_quickack_mode(sk);
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
TCP_DELACK_MAX, TCP_RTO_MAX);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 51a77e20f6c6..1660613ddae4 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -271,10 +271,13 @@ EXPORT_SYMBOL(tcp_v4_connect);
*/
void tcp_v4_mtu_reduced(struct sock *sk)
{
- struct dst_entry *dst;
struct inet_sock *inet = inet_sk(sk);
- u32 mtu = tcp_sk(sk)->mtu_info;
+ struct dst_entry *dst;
+ u32 mtu;
+ if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))
+ return;
+ mtu = tcp_sk(sk)->mtu_info;
dst = inet_csk_update_pmtu(sk, mtu);
if (!dst)
return;
@@ -420,7 +423,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
switch (type) {
case ICMP_REDIRECT:
- do_redirect(icmp_skb, sk);
+ if (!sock_owned_by_user(sk))
+ do_redirect(icmp_skb, sk);
goto out;
case ICMP_SOURCE_QUENCH:
/* Just silently ignore these. */
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 9475a2748a9a..019db68bdb9f 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -472,6 +472,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
newtp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
newtp->rtt_min[0].rtt = ~0U;
newicsk->icsk_rto = TCP_TIMEOUT_INIT;
+ newicsk->icsk_ack.lrcvtime = tcp_time_stamp;
newtp->packets_out = 0;
newtp->retrans_out = 0;
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index ce20968de667..4aef80d30fab 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -257,7 +257,8 @@ void tcp_delack_timer_handler(struct sock *sk)
sk_mem_reclaim_partial(sk);
- if (sk->sk_state == TCP_CLOSE || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER))
+ if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) ||
+ !(icsk->icsk_ack.pending & ICSK_ACK_TIMER))
goto out;
if (time_after(icsk->icsk_ack.timeout, jiffies)) {
@@ -538,7 +539,8 @@ void tcp_write_timer_handler(struct sock *sk)
struct inet_connection_sock *icsk = inet_csk(sk);
int event;
- if (sk->sk_state == TCP_CLOSE || !icsk->icsk_pending)
+ if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) ||
+ !icsk->icsk_pending)
goto out;
if (time_after(icsk->icsk_timeout, jiffies)) {
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 01455f492e17..3d72aeffa3f1 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -112,6 +112,27 @@ static inline u32 cstamp_delta(unsigned long cstamp)
return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
}
+static inline s32 rfc3315_s14_backoff_init(s32 irt)
+{
+ /* multiply 'initial retransmission time' by 0.9 .. 1.1 */
+ u64 tmp = (900000 + prandom_u32() % 200001) * (u64)irt;
+ do_div(tmp, 1000000);
+ return (s32)tmp;
+}
+
+static inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt)
+{
+ /* multiply 'retransmission timeout' by 1.9 .. 2.1 */
+ u64 tmp = (1900000 + prandom_u32() % 200001) * (u64)rt;
+ do_div(tmp, 1000000);
+ if ((s32)tmp > mrt) {
+ /* multiply 'maximum retransmission time' by 0.9 .. 1.1 */
+ tmp = (900000 + prandom_u32() % 200001) * (u64)mrt;
+ do_div(tmp, 1000000);
+ }
+ return (s32)tmp;
+}
+
#ifdef CONFIG_SYSCTL
static int addrconf_sysctl_register(struct inet6_dev *idev);
static void addrconf_sysctl_unregister(struct inet6_dev *idev);
@@ -187,6 +208,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.dad_transmits = 1,
.rtr_solicits = MAX_RTR_SOLICITATIONS,
.rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+ .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
.rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
.use_tempaddr = 0,
.temp_valid_lft = TEMP_VALID_LIFETIME,
@@ -202,6 +224,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.accept_ra_rtr_pref = 1,
.rtr_probe_interval = 60 * HZ,
#ifdef CONFIG_IPV6_ROUTE_INFO
+ .accept_ra_rt_info_min_plen = 0,
.accept_ra_rt_info_max_plen = 0,
#endif
#endif
@@ -233,6 +256,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
.dad_transmits = 1,
.rtr_solicits = MAX_RTR_SOLICITATIONS,
.rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+ .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
.rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
.use_tempaddr = 0,
.temp_valid_lft = TEMP_VALID_LIFETIME,
@@ -248,6 +272,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
.accept_ra_rtr_pref = 1,
.rtr_probe_interval = 60 * HZ,
#ifdef CONFIG_IPV6_ROUTE_INFO
+ .accept_ra_rt_info_min_plen = 0,
.accept_ra_rt_info_max_plen = 0,
#endif
#endif
@@ -3502,7 +3527,7 @@ static void addrconf_rs_timer(unsigned long data)
if (idev->if_flags & IF_RA_RCVD)
goto out;
- if (idev->rs_probes++ < idev->cnf.rtr_solicits) {
+ if (idev->rs_probes++ < idev->cnf.rtr_solicits || idev->cnf.rtr_solicits < 0) {
write_unlock(&idev->lock);
if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
ndisc_send_rs(dev, &lladdr,
@@ -3511,11 +3536,13 @@ static void addrconf_rs_timer(unsigned long data)
goto put;
write_lock(&idev->lock);
+ idev->rs_interval = rfc3315_s14_backoff_update(
+ idev->rs_interval, idev->cnf.rtr_solicit_max_interval);
/* The wait after the last probe can be shorter */
addrconf_mod_rs_timer(idev, (idev->rs_probes ==
idev->cnf.rtr_solicits) ?
idev->cnf.rtr_solicit_delay :
- idev->cnf.rtr_solicit_interval);
+ idev->rs_interval);
} else {
/*
* Note: we do not support deprecated "all on-link"
@@ -3743,7 +3770,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp);
send_rs = send_mld &&
ipv6_accept_ra(ifp->idev) &&
- ifp->idev->cnf.rtr_solicits > 0 &&
+ ifp->idev->cnf.rtr_solicits != 0 &&
(dev->flags&IFF_LOOPBACK) == 0;
read_unlock_bh(&ifp->idev->lock);
@@ -3765,10 +3792,11 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
write_lock_bh(&ifp->idev->lock);
spin_lock(&ifp->lock);
+ ifp->idev->rs_interval = rfc3315_s14_backoff_init(
+ ifp->idev->cnf.rtr_solicit_interval);
ifp->idev->rs_probes = 1;
ifp->idev->if_flags |= IF_RS_SENT;
- addrconf_mod_rs_timer(ifp->idev,
- ifp->idev->cnf.rtr_solicit_interval);
+ addrconf_mod_rs_timer(ifp->idev, ifp->idev->rs_interval);
spin_unlock(&ifp->lock);
write_unlock_bh(&ifp->idev->lock);
}
@@ -4685,6 +4713,8 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits;
array[DEVCONF_RTR_SOLICIT_INTERVAL] =
jiffies_to_msecs(cnf->rtr_solicit_interval);
+ array[DEVCONF_RTR_SOLICIT_MAX_INTERVAL] =
+ jiffies_to_msecs(cnf->rtr_solicit_max_interval);
array[DEVCONF_RTR_SOLICIT_DELAY] =
jiffies_to_msecs(cnf->rtr_solicit_delay);
array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version;
@@ -4706,6 +4736,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
array[DEVCONF_RTR_PROBE_INTERVAL] =
jiffies_to_msecs(cnf->rtr_probe_interval);
#ifdef CONFIG_IPV6_ROUTE_INFO
+ array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen;
array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen;
#endif
#endif
@@ -4893,7 +4924,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
return -EINVAL;
if (!ipv6_accept_ra(idev))
return -EINVAL;
- if (idev->cnf.rtr_solicits <= 0)
+ if (idev->cnf.rtr_solicits == 0)
return -EINVAL;
write_lock_bh(&idev->lock);
@@ -4918,8 +4949,10 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
if (update_rs) {
idev->if_flags |= IF_RS_SENT;
+ idev->rs_interval = rfc3315_s14_backoff_init(
+ idev->cnf.rtr_solicit_interval);
idev->rs_probes = 1;
- addrconf_mod_rs_timer(idev, idev->cnf.rtr_solicit_interval);
+ addrconf_mod_rs_timer(idev, idev->rs_interval);
}
/* Well, that's kinda nasty ... */
@@ -5557,6 +5590,13 @@ static struct addrconf_sysctl_table
.proc_handler = proc_dointvec_jiffies,
},
{
+ .procname = "router_solicitation_max_interval",
+ .data = &ipv6_devconf.rtr_solicit_max_interval,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
+ {
.procname = "router_solicitation_delay",
.data = &ipv6_devconf.rtr_solicit_delay,
.maxlen = sizeof(int),
@@ -5666,6 +5706,13 @@ static struct addrconf_sysctl_table
},
#ifdef CONFIG_IPV6_ROUTE_INFO
{
+ .procname = "accept_ra_rt_info_min_plen",
+ .data = &ipv6_devconf.accept_ra_rt_info_min_plen,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "accept_ra_rt_info_max_plen",
.data = &ipv6_devconf.accept_ra_rt_info_max_plen,
.maxlen = sizeof(int),
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 34cf46d74554..85bf86458706 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -903,6 +903,8 @@ add:
ins = &rt->dst.rt6_next;
iter = *ins;
while (iter) {
+ if (iter->rt6i_metric > rt->rt6i_metric)
+ break;
if (rt6_qualify_for_ecmp(iter)) {
*ins = iter->dst.rt6_next;
fib6_purge_rt(iter, fn, info->nl_net);
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index cd96a01032a2..cf90a9bf26a3 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -742,13 +742,14 @@ slow_path:
* Fragment the datagram.
*/
- *prevhdr = NEXTHDR_FRAGMENT;
troom = rt->dst.dev->needed_tailroom;
/*
* Keep copying data until we run out.
*/
while (left > 0) {
+ u8 *fragnexthdr_offset;
+
len = left;
/* IF: it doesn't fit, use 'mtu' - the data space left */
if (len > mtu)
@@ -793,6 +794,10 @@ slow_path:
*/
skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);
+ fragnexthdr_offset = skb_network_header(frag);
+ fragnexthdr_offset += prevhdr - skb_network_header(skb);
+ *fragnexthdr_offset = NEXTHDR_FRAGMENT;
+
/*
* Build fragment header.
*/
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index 24fb9c0efd00..5b7433887eda 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -681,6 +681,10 @@ vti6_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p)
u->link = p->link;
u->i_key = p->i_key;
u->o_key = p->o_key;
+ if (u->i_key)
+ u->i_flags |= GRE_KEY;
+ if (u->o_key)
+ u->o_flags |= GRE_KEY;
u->proto = p->proto;
memcpy(u->name, p->name, sizeof(u->name));
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 84afb9a77278..3452f9037ad4 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1358,6 +1358,8 @@ skip_linkparms:
if (ri->prefix_len == 0 &&
!in6_dev->cnf.accept_ra_defrtr)
continue;
+ if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen)
+ continue;
if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
continue;
rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3,
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index e10a04c9cdc7..cf336d670f8b 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -135,6 +135,18 @@ config IP6_NF_IPTABLES
if IP6_NF_IPTABLES
+config IP6_NF_IPTABLES_128
+ tristate "128 bit arithmetic for iptables matching"
+ depends on IP6_NF_IPTABLES
+ help
+ This enables 128 bit matching in ip6tables to help optimize cases
+ where there is no match required. ip6tables matching for ipv6 always
+ has a mask if an address is specified for match. Adding a check for
+ mask prior to that helps to improve performance as it avoids the
+ masked comparison.
+
+ Note that this feature depends on the architecture. If unsure, say N.
+
# The simple matches.
config IP6_NF_MATCH_AH
tristate '"ah" match support'
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 22f39e00bef3..6fd784643d6e 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -94,22 +94,26 @@ ip6_packet_match(const struct sk_buff *skb,
{
unsigned long ret;
const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+ const __uint128_t *ulm1 = (const __uint128_t *)&ip6info->smsk;
+ const __uint128_t *ulm2 = (const __uint128_t *)&ip6info->dmsk;
+#endif
#define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
- if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
- &ip6info->src), IP6T_INV_SRCIP) ||
- FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
- &ip6info->dst), IP6T_INV_DSTIP)) {
- dprintf("Source or dest mismatch.\n");
-/*
- dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
- ipinfo->smsk.s_addr, ipinfo->src.s_addr,
- ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
- dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
- ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
- ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
- return false;
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+ if (*ulm1 || *ulm2)
+#endif
+ {
+ if (FWINV(ipv6_masked_addr_cmp
+ (&ipv6->saddr, &ip6info->smsk, &ip6info->src),
+ IP6T_INV_SRCIP) ||
+ FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
+ &ip6info->dst),
+ IP6T_INV_DSTIP)) {
+ dprintf("Source or dest mismatch.\n");
+ return false;
+ }
}
ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 108b39967694..8532768b4eaa 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -377,10 +377,12 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
np = inet6_sk(sk);
if (type == NDISC_REDIRECT) {
- struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
+ if (!sock_owned_by_user(sk)) {
+ struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
- if (dst)
- dst->ops->redirect(dst, sk, skb);
+ if (dst)
+ dst->ops->redirect(dst, sk, skb);
+ }
goto out;
}
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 445b7cd0826a..48ab93842322 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -383,7 +383,7 @@ static int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb)
drop:
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS);
kfree_skb(skb);
- return -1;
+ return 0;
}
/* Userspace will call sendmsg() on the tunnel socket to send L2TP
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 881bc2072809..52cfc4478511 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -1567,6 +1567,7 @@ static void mpls_net_exit(struct net *net)
for (index = 0; index < platform_labels; index++) {
struct mpls_route *rt = rtnl_dereference(platform_label[index]);
RCU_INIT_POINTER(platform_label[index], NULL);
+ mpls_notify_route(net, index, rt, NULL, NULL);
mpls_rt_free(rt);
}
rtnl_unlock();
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index ececa65868ef..e0d8e9ad315b 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -1802,8 +1802,11 @@ ret_res:
}
#ifdef DDEBUG
-/* This function is not in xt_qtaguid_print.c because of locks visibility */
-static void prdebug_full_state(int indent_level, const char *fmt, ...)
+/*
+ * This function is not in xt_qtaguid_print.c because of locks visibility.
+ * The lock of sock_tag_list must be aquired before calling this function
+ */
+static void prdebug_full_state_locked(int indent_level, const char *fmt, ...)
{
va_list args;
char *fmt_buff;
@@ -1824,16 +1827,12 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...)
kfree(buff);
va_end(args);
- spin_lock_bh(&sock_tag_list_lock);
prdebug_sock_tag_tree(indent_level, &sock_tag_tree);
- spin_unlock_bh(&sock_tag_list_lock);
- spin_lock_bh(&sock_tag_list_lock);
spin_lock_bh(&uid_tag_data_tree_lock);
prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree);
prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree);
spin_unlock_bh(&uid_tag_data_tree_lock);
- spin_unlock_bh(&sock_tag_list_lock);
spin_lock_bh(&iface_stat_list_lock);
prdebug_iface_stat_list(indent_level, &iface_stat_list);
@@ -1842,7 +1841,7 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...)
pr_debug("qtaguid: %s(): }\n", __func__);
}
#else
-static void prdebug_full_state(int indent_level, const char *fmt, ...) {}
+static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) {}
#endif
struct proc_ctrl_print_info {
@@ -1965,8 +1964,11 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
(u64)atomic64_read(&qtu_events.match_no_sk),
(u64)atomic64_read(&qtu_events.match_no_sk_file));
- /* Count the following as part of the last item_index */
- prdebug_full_state(0, "proc ctrl");
+ /* Count the following as part of the last item_index. No need
+ * to lock the sock_tag_list here since it is already locked when
+ * starting the seq_file operation
+ */
+ prdebug_full_state_locked(0, "proc ctrl");
}
return 0;
@@ -2875,8 +2877,10 @@ static int qtudev_release(struct inode *inode, struct file *file)
sock_tag_tree_erase(&st_to_free_tree);
- prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__,
+ spin_lock_bh(&sock_tag_list_lock);
+ prdebug_full_state_locked(0, "%s(): pid=%u tgid=%u", __func__,
current->pid, current->tgid);
+ spin_unlock_bh(&sock_tag_list_lock);
return 0;
}
diff --git a/net/netlink/Kconfig b/net/netlink/Kconfig
index 2c5e95e9bfbd..5d6e8c05b3d4 100644
--- a/net/netlink/Kconfig
+++ b/net/netlink/Kconfig
@@ -2,15 +2,6 @@
# Netlink Sockets
#
-config NETLINK_MMAP
- bool "NETLINK: mmaped IO"
- ---help---
- This option enables support for memory mapped netlink IO. This
- reduces overhead by avoiding copying data between kernel- and
- userspace.
-
- If unsure, say N.
-
config NETLINK_DIAG
tristate "NETLINK: socket monitoring interface"
default n
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index f4dd706c6cd1..96fe1c103bf9 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -225,7 +225,7 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb,
dev_hold(dev);
- if (netlink_skb_is_mmaped(skb) || is_vmalloc_addr(skb->head))
+ if (is_vmalloc_addr(skb->head))
nskb = netlink_to_full_skb(skb, GFP_ATOMIC);
else
nskb = skb_clone(skb, GFP_ATOMIC);
@@ -300,610 +300,8 @@ static void netlink_rcv_wake(struct sock *sk)
wake_up_interruptible(&nlk->wait);
}
-#ifdef CONFIG_NETLINK_MMAP
-static bool netlink_rx_is_mmaped(struct sock *sk)
-{
- return nlk_sk(sk)->rx_ring.pg_vec != NULL;
-}
-
-static bool netlink_tx_is_mmaped(struct sock *sk)
-{
- return nlk_sk(sk)->tx_ring.pg_vec != NULL;
-}
-
-static __pure struct page *pgvec_to_page(const void *addr)
-{
- if (is_vmalloc_addr(addr))
- return vmalloc_to_page(addr);
- else
- return virt_to_page(addr);
-}
-
-static void free_pg_vec(void **pg_vec, unsigned int order, unsigned int len)
-{
- unsigned int i;
-
- for (i = 0; i < len; i++) {
- if (pg_vec[i] != NULL) {
- if (is_vmalloc_addr(pg_vec[i]))
- vfree(pg_vec[i]);
- else
- free_pages((unsigned long)pg_vec[i], order);
- }
- }
- kfree(pg_vec);
-}
-
-static void *alloc_one_pg_vec_page(unsigned long order)
-{
- void *buffer;
- gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | __GFP_ZERO |
- __GFP_NOWARN | __GFP_NORETRY;
-
- buffer = (void *)__get_free_pages(gfp_flags, order);
- if (buffer != NULL)
- return buffer;
-
- buffer = vzalloc((1 << order) * PAGE_SIZE);
- if (buffer != NULL)
- return buffer;
-
- gfp_flags &= ~__GFP_NORETRY;
- return (void *)__get_free_pages(gfp_flags, order);
-}
-
-static void **alloc_pg_vec(struct netlink_sock *nlk,
- struct nl_mmap_req *req, unsigned int order)
-{
- unsigned int block_nr = req->nm_block_nr;
- unsigned int i;
- void **pg_vec;
-
- pg_vec = kcalloc(block_nr, sizeof(void *), GFP_KERNEL);
- if (pg_vec == NULL)
- return NULL;
-
- for (i = 0; i < block_nr; i++) {
- pg_vec[i] = alloc_one_pg_vec_page(order);
- if (pg_vec[i] == NULL)
- goto err1;
- }
-
- return pg_vec;
-err1:
- free_pg_vec(pg_vec, order, block_nr);
- return NULL;
-}
-
-
-static void
-__netlink_set_ring(struct sock *sk, struct nl_mmap_req *req, bool tx_ring, void **pg_vec,
- unsigned int order)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- struct sk_buff_head *queue;
- struct netlink_ring *ring;
-
- queue = tx_ring ? &sk->sk_write_queue : &sk->sk_receive_queue;
- ring = tx_ring ? &nlk->tx_ring : &nlk->rx_ring;
-
- spin_lock_bh(&queue->lock);
-
- ring->frame_max = req->nm_frame_nr - 1;
- ring->head = 0;
- ring->frame_size = req->nm_frame_size;
- ring->pg_vec_pages = req->nm_block_size / PAGE_SIZE;
-
- swap(ring->pg_vec_len, req->nm_block_nr);
- swap(ring->pg_vec_order, order);
- swap(ring->pg_vec, pg_vec);
-
- __skb_queue_purge(queue);
- spin_unlock_bh(&queue->lock);
-
- WARN_ON(atomic_read(&nlk->mapped));
-
- if (pg_vec)
- free_pg_vec(pg_vec, order, req->nm_block_nr);
-}
-
-static int netlink_set_ring(struct sock *sk, struct nl_mmap_req *req,
- bool tx_ring)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- struct netlink_ring *ring;
- void **pg_vec = NULL;
- unsigned int order = 0;
-
- ring = tx_ring ? &nlk->tx_ring : &nlk->rx_ring;
-
- if (atomic_read(&nlk->mapped))
- return -EBUSY;
- if (atomic_read(&ring->pending))
- return -EBUSY;
-
- if (req->nm_block_nr) {
- if (ring->pg_vec != NULL)
- return -EBUSY;
-
- if ((int)req->nm_block_size <= 0)
- return -EINVAL;
- if (!PAGE_ALIGNED(req->nm_block_size))
- return -EINVAL;
- if (req->nm_frame_size < NL_MMAP_HDRLEN)
- return -EINVAL;
- if (!IS_ALIGNED(req->nm_frame_size, NL_MMAP_MSG_ALIGNMENT))
- return -EINVAL;
-
- ring->frames_per_block = req->nm_block_size /
- req->nm_frame_size;
- if (ring->frames_per_block == 0)
- return -EINVAL;
- if (ring->frames_per_block * req->nm_block_nr !=
- req->nm_frame_nr)
- return -EINVAL;
-
- order = get_order(req->nm_block_size);
- pg_vec = alloc_pg_vec(nlk, req, order);
- if (pg_vec == NULL)
- return -ENOMEM;
- } else {
- if (req->nm_frame_nr)
- return -EINVAL;
- }
-
- mutex_lock(&nlk->pg_vec_lock);
- if (atomic_read(&nlk->mapped) == 0) {
- __netlink_set_ring(sk, req, tx_ring, pg_vec, order);
- mutex_unlock(&nlk->pg_vec_lock);
- return 0;
- }
-
- mutex_unlock(&nlk->pg_vec_lock);
-
- if (pg_vec)
- free_pg_vec(pg_vec, order, req->nm_block_nr);
-
- return -EBUSY;
-}
-
-static void netlink_mm_open(struct vm_area_struct *vma)
-{
- struct file *file = vma->vm_file;
- struct socket *sock = file->private_data;
- struct sock *sk = sock->sk;
-
- if (sk)
- atomic_inc(&nlk_sk(sk)->mapped);
-}
-
-static void netlink_mm_close(struct vm_area_struct *vma)
-{
- struct file *file = vma->vm_file;
- struct socket *sock = file->private_data;
- struct sock *sk = sock->sk;
-
- if (sk)
- atomic_dec(&nlk_sk(sk)->mapped);
-}
-
-static const struct vm_operations_struct netlink_mmap_ops = {
- .open = netlink_mm_open,
- .close = netlink_mm_close,
-};
-
-static int netlink_mmap(struct file *file, struct socket *sock,
- struct vm_area_struct *vma)
-{
- struct sock *sk = sock->sk;
- struct netlink_sock *nlk = nlk_sk(sk);
- struct netlink_ring *ring;
- unsigned long start, size, expected;
- unsigned int i;
- int err = -EINVAL;
-
- if (vma->vm_pgoff)
- return -EINVAL;
-
- mutex_lock(&nlk->pg_vec_lock);
-
- expected = 0;
- for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) {
- if (ring->pg_vec == NULL)
- continue;
- expected += ring->pg_vec_len * ring->pg_vec_pages * PAGE_SIZE;
- }
-
- if (expected == 0)
- goto out;
-
- size = vma->vm_end - vma->vm_start;
- if (size != expected)
- goto out;
-
- start = vma->vm_start;
- for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) {
- if (ring->pg_vec == NULL)
- continue;
-
- for (i = 0; i < ring->pg_vec_len; i++) {
- struct page *page;
- void *kaddr = ring->pg_vec[i];
- unsigned int pg_num;
-
- for (pg_num = 0; pg_num < ring->pg_vec_pages; pg_num++) {
- page = pgvec_to_page(kaddr);
- err = vm_insert_page(vma, start, page);
- if (err < 0)
- goto out;
- start += PAGE_SIZE;
- kaddr += PAGE_SIZE;
- }
- }
- }
-
- atomic_inc(&nlk->mapped);
- vma->vm_ops = &netlink_mmap_ops;
- err = 0;
-out:
- mutex_unlock(&nlk->pg_vec_lock);
- return err;
-}
-
-static void netlink_frame_flush_dcache(const struct nl_mmap_hdr *hdr, unsigned int nm_len)
-{
-#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE == 1
- struct page *p_start, *p_end;
-
- /* First page is flushed through netlink_{get,set}_status */
- p_start = pgvec_to_page(hdr + PAGE_SIZE);
- p_end = pgvec_to_page((void *)hdr + NL_MMAP_HDRLEN + nm_len - 1);
- while (p_start <= p_end) {
- flush_dcache_page(p_start);
- p_start++;
- }
-#endif
-}
-
-static enum nl_mmap_status netlink_get_status(const struct nl_mmap_hdr *hdr)
-{
- smp_rmb();
- flush_dcache_page(pgvec_to_page(hdr));
- return hdr->nm_status;
-}
-
-static void netlink_set_status(struct nl_mmap_hdr *hdr,
- enum nl_mmap_status status)
-{
- smp_mb();
- hdr->nm_status = status;
- flush_dcache_page(pgvec_to_page(hdr));
-}
-
-static struct nl_mmap_hdr *
-__netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos)
-{
- unsigned int pg_vec_pos, frame_off;
-
- pg_vec_pos = pos / ring->frames_per_block;
- frame_off = pos % ring->frames_per_block;
-
- return ring->pg_vec[pg_vec_pos] + (frame_off * ring->frame_size);
-}
-
-static struct nl_mmap_hdr *
-netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos,
- enum nl_mmap_status status)
-{
- struct nl_mmap_hdr *hdr;
-
- hdr = __netlink_lookup_frame(ring, pos);
- if (netlink_get_status(hdr) != status)
- return NULL;
-
- return hdr;
-}
-
-static struct nl_mmap_hdr *
-netlink_current_frame(const struct netlink_ring *ring,
- enum nl_mmap_status status)
-{
- return netlink_lookup_frame(ring, ring->head, status);
-}
-
-static void netlink_increment_head(struct netlink_ring *ring)
-{
- ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0;
-}
-
-static void netlink_forward_ring(struct netlink_ring *ring)
-{
- unsigned int head = ring->head;
- const struct nl_mmap_hdr *hdr;
-
- do {
- hdr = __netlink_lookup_frame(ring, ring->head);
- if (hdr->nm_status == NL_MMAP_STATUS_UNUSED)
- break;
- if (hdr->nm_status != NL_MMAP_STATUS_SKIP)
- break;
- netlink_increment_head(ring);
- } while (ring->head != head);
-}
-
-static bool netlink_has_valid_frame(struct netlink_ring *ring)
-{
- unsigned int head = ring->head, pos = head;
- const struct nl_mmap_hdr *hdr;
-
- do {
- hdr = __netlink_lookup_frame(ring, pos);
- if (hdr->nm_status == NL_MMAP_STATUS_VALID)
- return true;
- pos = pos != 0 ? pos - 1 : ring->frame_max;
- } while (pos != head);
-
- return false;
-}
-
-static bool netlink_dump_space(struct netlink_sock *nlk)
-{
- struct netlink_ring *ring = &nlk->rx_ring;
- struct nl_mmap_hdr *hdr;
- unsigned int n;
-
- hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
- if (hdr == NULL)
- return false;
-
- n = ring->head + ring->frame_max / 2;
- if (n > ring->frame_max)
- n -= ring->frame_max;
-
- hdr = __netlink_lookup_frame(ring, n);
-
- return hdr->nm_status == NL_MMAP_STATUS_UNUSED;
-}
-
-static unsigned int netlink_poll(struct file *file, struct socket *sock,
- poll_table *wait)
-{
- struct sock *sk = sock->sk;
- struct netlink_sock *nlk = nlk_sk(sk);
- unsigned int mask;
- int err;
-
- if (nlk->rx_ring.pg_vec != NULL) {
- /* Memory mapped sockets don't call recvmsg(), so flow control
- * for dumps is performed here. A dump is allowed to continue
- * if at least half the ring is unused.
- */
- while (nlk->cb_running && netlink_dump_space(nlk)) {
- err = netlink_dump(sk);
- if (err < 0) {
- sk->sk_err = -err;
- sk->sk_error_report(sk);
- break;
- }
- }
- netlink_rcv_wake(sk);
- }
-
- mask = datagram_poll(file, sock, wait);
-
- /* We could already have received frames in the normal receive
- * queue, that will show up as NL_MMAP_STATUS_COPY in the ring,
- * so if mask contains pollin/etc already, there's no point
- * walking the ring.
- */
- if ((mask & (POLLIN | POLLRDNORM)) != (POLLIN | POLLRDNORM)) {
- spin_lock_bh(&sk->sk_receive_queue.lock);
- if (nlk->rx_ring.pg_vec) {
- if (netlink_has_valid_frame(&nlk->rx_ring))
- mask |= POLLIN | POLLRDNORM;
- }
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- }
-
- spin_lock_bh(&sk->sk_write_queue.lock);
- if (nlk->tx_ring.pg_vec) {
- if (netlink_current_frame(&nlk->tx_ring, NL_MMAP_STATUS_UNUSED))
- mask |= POLLOUT | POLLWRNORM;
- }
- spin_unlock_bh(&sk->sk_write_queue.lock);
-
- return mask;
-}
-
-static struct nl_mmap_hdr *netlink_mmap_hdr(struct sk_buff *skb)
-{
- return (struct nl_mmap_hdr *)(skb->head - NL_MMAP_HDRLEN);
-}
-
-static void netlink_ring_setup_skb(struct sk_buff *skb, struct sock *sk,
- struct netlink_ring *ring,
- struct nl_mmap_hdr *hdr)
-{
- unsigned int size;
- void *data;
-
- size = ring->frame_size - NL_MMAP_HDRLEN;
- data = (void *)hdr + NL_MMAP_HDRLEN;
-
- skb->head = data;
- skb->data = data;
- skb_reset_tail_pointer(skb);
- skb->end = skb->tail + size;
- skb->len = 0;
-
- skb->destructor = netlink_skb_destructor;
- NETLINK_CB(skb).flags |= NETLINK_SKB_MMAPED;
- NETLINK_CB(skb).sk = sk;
-}
-
-static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg,
- u32 dst_portid, u32 dst_group,
- struct scm_cookie *scm)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- struct netlink_ring *ring;
- struct nl_mmap_hdr *hdr;
- struct sk_buff *skb;
- unsigned int maxlen;
- int err = 0, len = 0;
-
- mutex_lock(&nlk->pg_vec_lock);
-
- ring = &nlk->tx_ring;
- maxlen = ring->frame_size - NL_MMAP_HDRLEN;
-
- do {
- unsigned int nm_len;
-
- hdr = netlink_current_frame(ring, NL_MMAP_STATUS_VALID);
- if (hdr == NULL) {
- if (!(msg->msg_flags & MSG_DONTWAIT) &&
- atomic_read(&nlk->tx_ring.pending))
- schedule();
- continue;
- }
-
- nm_len = ACCESS_ONCE(hdr->nm_len);
- if (nm_len > maxlen) {
- err = -EINVAL;
- goto out;
- }
-
- netlink_frame_flush_dcache(hdr, nm_len);
-
- skb = alloc_skb(nm_len, GFP_KERNEL);
- if (skb == NULL) {
- err = -ENOBUFS;
- goto out;
- }
- __skb_put(skb, nm_len);
- memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, nm_len);
- netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED);
-
- netlink_increment_head(ring);
-
- NETLINK_CB(skb).portid = nlk->portid;
- NETLINK_CB(skb).dst_group = dst_group;
- NETLINK_CB(skb).creds = scm->creds;
-
- err = security_netlink_send(sk, skb);
- if (err) {
- kfree_skb(skb);
- goto out;
- }
-
- if (unlikely(dst_group)) {
- atomic_inc(&skb->users);
- netlink_broadcast(sk, skb, dst_portid, dst_group,
- GFP_KERNEL);
- }
- err = netlink_unicast(sk, skb, dst_portid,
- msg->msg_flags & MSG_DONTWAIT);
- if (err < 0)
- goto out;
- len += err;
-
- } while (hdr != NULL ||
- (!(msg->msg_flags & MSG_DONTWAIT) &&
- atomic_read(&nlk->tx_ring.pending)));
-
- if (len > 0)
- err = len;
-out:
- mutex_unlock(&nlk->pg_vec_lock);
- return err;
-}
-
-static void netlink_queue_mmaped_skb(struct sock *sk, struct sk_buff *skb)
-{
- struct nl_mmap_hdr *hdr;
-
- hdr = netlink_mmap_hdr(skb);
- hdr->nm_len = skb->len;
- hdr->nm_group = NETLINK_CB(skb).dst_group;
- hdr->nm_pid = NETLINK_CB(skb).creds.pid;
- hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid);
- hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid);
- netlink_frame_flush_dcache(hdr, hdr->nm_len);
- netlink_set_status(hdr, NL_MMAP_STATUS_VALID);
-
- NETLINK_CB(skb).flags |= NETLINK_SKB_DELIVERED;
- kfree_skb(skb);
-}
-
-static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- struct netlink_ring *ring = &nlk->rx_ring;
- struct nl_mmap_hdr *hdr;
-
- spin_lock_bh(&sk->sk_receive_queue.lock);
- hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
- if (hdr == NULL) {
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- kfree_skb(skb);
- netlink_overrun(sk);
- return;
- }
- netlink_increment_head(ring);
- __skb_queue_tail(&sk->sk_receive_queue, skb);
- spin_unlock_bh(&sk->sk_receive_queue.lock);
-
- hdr->nm_len = skb->len;
- hdr->nm_group = NETLINK_CB(skb).dst_group;
- hdr->nm_pid = NETLINK_CB(skb).creds.pid;
- hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid);
- hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid);
- netlink_set_status(hdr, NL_MMAP_STATUS_COPY);
-}
-
-#else /* CONFIG_NETLINK_MMAP */
-#define netlink_rx_is_mmaped(sk) false
-#define netlink_tx_is_mmaped(sk) false
-#define netlink_mmap sock_no_mmap
-#define netlink_poll datagram_poll
-#define netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, scm) 0
-#endif /* CONFIG_NETLINK_MMAP */
-
static void netlink_skb_destructor(struct sk_buff *skb)
{
-#ifdef CONFIG_NETLINK_MMAP
- struct nl_mmap_hdr *hdr;
- struct netlink_ring *ring;
- struct sock *sk;
-
- /* If a packet from the kernel to userspace was freed because of an
- * error without being delivered to userspace, the kernel must reset
- * the status. In the direction userspace to kernel, the status is
- * always reset here after the packet was processed and freed.
- */
- if (netlink_skb_is_mmaped(skb)) {
- hdr = netlink_mmap_hdr(skb);
- sk = NETLINK_CB(skb).sk;
-
- if (NETLINK_CB(skb).flags & NETLINK_SKB_TX) {
- netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED);
- ring = &nlk_sk(sk)->tx_ring;
- } else {
- if (!(NETLINK_CB(skb).flags & NETLINK_SKB_DELIVERED)) {
- hdr->nm_len = 0;
- netlink_set_status(hdr, NL_MMAP_STATUS_VALID);
- }
- ring = &nlk_sk(sk)->rx_ring;
- }
-
- WARN_ON(atomic_read(&ring->pending) == 0);
- atomic_dec(&ring->pending);
- sock_put(sk);
-
- skb->head = NULL;
- }
-#endif
if (is_vmalloc_addr(skb->head)) {
if (!skb->cloned ||
!atomic_dec_return(&(skb_shinfo(skb)->dataref)))
@@ -927,18 +325,6 @@ static void netlink_skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
static void netlink_sock_destruct(struct sock *sk)
{
skb_queue_purge(&sk->sk_receive_queue);
-#ifdef CONFIG_NETLINK_MMAP
- if (1) {
- struct nl_mmap_req req;
-
- memset(&req, 0, sizeof(req));
- if (nlk->rx_ring.pg_vec)
- __netlink_set_ring(sk, &req, false, NULL, 0);
- memset(&req, 0, sizeof(req));
- if (nlk->tx_ring.pg_vec)
- __netlink_set_ring(sk, &req, true, NULL, 0);
- }
-#endif /* CONFIG_NETLINK_MMAP */
if (!sock_flag(sk, SOCK_DEAD)) {
printk(KERN_ERR "Freeing alive netlink socket %p\n", sk);
@@ -1185,9 +571,6 @@ static int __netlink_create(struct net *net, struct socket *sock,
mutex_init(nlk->cb_mutex);
}
init_waitqueue_head(&nlk->wait);
-#ifdef CONFIG_NETLINK_MMAP
- mutex_init(&nlk->pg_vec_lock);
-#endif
sock_set_flag(sk, SOCK_RCU_FREE);
sk->sk_destruct = netlink_sock_destruct;
@@ -1725,8 +1108,7 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
nlk = nlk_sk(sk);
if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
- test_bit(NETLINK_S_CONGESTED, &nlk->state)) &&
- !netlink_skb_is_mmaped(skb)) {
+ test_bit(NETLINK_S_CONGESTED, &nlk->state))) {
DECLARE_WAITQUEUE(wait, current);
if (!*timeo) {
if (!ssk || netlink_is_kernel(ssk))
@@ -1764,14 +1146,7 @@ static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb)
netlink_deliver_tap(skb);
-#ifdef CONFIG_NETLINK_MMAP
- if (netlink_skb_is_mmaped(skb))
- netlink_queue_mmaped_skb(sk, skb);
- else if (netlink_rx_is_mmaped(sk))
- netlink_ring_set_copied(sk, skb);
- else
-#endif /* CONFIG_NETLINK_MMAP */
- skb_queue_tail(&sk->sk_receive_queue, skb);
+ skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk);
return len;
}
@@ -1795,9 +1170,6 @@ static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation)
int delta;
WARN_ON(skb->sk != NULL);
- if (netlink_skb_is_mmaped(skb))
- return skb;
-
delta = skb->end - skb->tail;
if (is_vmalloc_addr(skb->head) || delta * 2 < skb->truesize)
return skb;
@@ -1877,71 +1249,6 @@ struct sk_buff *__netlink_alloc_skb(struct sock *ssk, unsigned int size,
unsigned int ldiff, u32 dst_portid,
gfp_t gfp_mask)
{
-#ifdef CONFIG_NETLINK_MMAP
- unsigned int maxlen, linear_size;
- struct sock *sk = NULL;
- struct sk_buff *skb;
- struct netlink_ring *ring;
- struct nl_mmap_hdr *hdr;
-
- sk = netlink_getsockbyportid(ssk, dst_portid);
- if (IS_ERR(sk))
- goto out;
-
- ring = &nlk_sk(sk)->rx_ring;
- /* fast-path without atomic ops for common case: non-mmaped receiver */
- if (ring->pg_vec == NULL)
- goto out_put;
-
- /* We need to account the full linear size needed as a ring
- * slot cannot have non-linear parts.
- */
- linear_size = size + ldiff;
- if (ring->frame_size - NL_MMAP_HDRLEN < linear_size)
- goto out_put;
-
- skb = alloc_skb_head(gfp_mask);
- if (skb == NULL)
- goto err1;
-
- spin_lock_bh(&sk->sk_receive_queue.lock);
- /* check again under lock */
- if (ring->pg_vec == NULL)
- goto out_free;
-
- /* check again under lock */
- maxlen = ring->frame_size - NL_MMAP_HDRLEN;
- if (maxlen < linear_size)
- goto out_free;
-
- netlink_forward_ring(ring);
- hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
- if (hdr == NULL)
- goto err2;
-
- netlink_ring_setup_skb(skb, sk, ring, hdr);
- netlink_set_status(hdr, NL_MMAP_STATUS_RESERVED);
- atomic_inc(&ring->pending);
- netlink_increment_head(ring);
-
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- return skb;
-
-err2:
- kfree_skb(skb);
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- netlink_overrun(sk);
-err1:
- sock_put(sk);
- return NULL;
-
-out_free:
- kfree_skb(skb);
- spin_unlock_bh(&sk->sk_receive_queue.lock);
-out_put:
- sock_put(sk);
-out:
-#endif
return alloc_skb(size, gfp_mask);
}
EXPORT_SYMBOL_GPL(__netlink_alloc_skb);
@@ -2222,8 +1529,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
if (level != SOL_NETLINK)
return -ENOPROTOOPT;
- if (optname != NETLINK_RX_RING && optname != NETLINK_TX_RING &&
- optlen >= sizeof(int) &&
+ if (optlen >= sizeof(int) &&
get_user(val, (unsigned int __user *)optval))
return -EFAULT;
@@ -2276,25 +1582,6 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
}
err = 0;
break;
-#ifdef CONFIG_NETLINK_MMAP
- case NETLINK_RX_RING:
- case NETLINK_TX_RING: {
- struct nl_mmap_req req;
-
- /* Rings might consume more memory than queue limits, require
- * CAP_NET_ADMIN.
- */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- if (optlen < sizeof(req))
- return -EINVAL;
- if (copy_from_user(&req, optval, sizeof(req)))
- return -EFAULT;
- err = netlink_set_ring(sk, &req,
- optname == NETLINK_TX_RING);
- break;
- }
-#endif /* CONFIG_NETLINK_MMAP */
case NETLINK_LISTEN_ALL_NSID:
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_BROADCAST))
return -EPERM;
@@ -2464,18 +1751,6 @@ static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
smp_rmb();
}
- /* It's a really convoluted way for userland to ask for mmaped
- * sendmsg(), but that's what we've got...
- */
- if (netlink_tx_is_mmaped(sk) &&
- iter_is_iovec(&msg->msg_iter) &&
- msg->msg_iter.nr_segs == 1 &&
- msg->msg_iter.iov->iov_base == NULL) {
- err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group,
- &scm);
- goto out;
- }
-
err = -EMSGSIZE;
if (len > sk->sk_sndbuf - 32)
goto out;
@@ -2792,8 +2067,7 @@ static int netlink_dump(struct sock *sk)
goto errout_skb;
}
- if (!netlink_rx_is_mmaped(sk) &&
- atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
+ if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
goto errout_skb;
/* NLMSG_GOODSIZE is small to avoid high order allocations being
@@ -2882,16 +2156,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
struct netlink_sock *nlk;
int ret;
- /* Memory mapped dump requests need to be copied to avoid looping
- * on the pending state in netlink_mmap_sendmsg() while the CB hold
- * a reference to the skb.
- */
- if (netlink_skb_is_mmaped(skb)) {
- skb = skb_copy(skb, GFP_KERNEL);
- if (skb == NULL)
- return -ENOBUFS;
- } else
- atomic_inc(&skb->users);
+ atomic_inc(&skb->users);
sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid);
if (sk == NULL) {
@@ -3235,7 +2500,7 @@ static const struct proto_ops netlink_ops = {
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = netlink_getname,
- .poll = netlink_poll,
+ .poll = datagram_poll,
.ioctl = sock_no_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
@@ -3243,7 +2508,7 @@ static const struct proto_ops netlink_ops = {
.getsockopt = netlink_getsockopt,
.sendmsg = netlink_sendmsg,
.recvmsg = netlink_recvmsg,
- .mmap = netlink_mmap,
+ .mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
index b0c1ddc97260..15e62973cfc6 100644
--- a/net/netlink/af_netlink.h
+++ b/net/netlink/af_netlink.h
@@ -45,12 +45,6 @@ struct netlink_sock {
int (*netlink_bind)(struct net *net, int group);
void (*netlink_unbind)(struct net *net, int group);
struct module *module;
-#ifdef CONFIG_NETLINK_MMAP
- struct mutex pg_vec_lock;
- struct netlink_ring rx_ring;
- struct netlink_ring tx_ring;
- atomic_t mapped;
-#endif /* CONFIG_NETLINK_MMAP */
struct rhash_head node;
struct work_struct work;
@@ -61,15 +55,6 @@ static inline struct netlink_sock *nlk_sk(struct sock *sk)
return container_of(sk, struct netlink_sock, sk);
}
-static inline bool netlink_skb_is_mmaped(const struct sk_buff *skb)
-{
-#ifdef CONFIG_NETLINK_MMAP
- return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED;
-#else
- return false;
-#endif /* CONFIG_NETLINK_MMAP */
-}
-
struct netlink_table {
struct rhashtable hash;
struct hlist_head mc_list;
diff --git a/net/netlink/diag.c b/net/netlink/diag.c
index 3ee63a3cff30..8dd836a8dd60 100644
--- a/net/netlink/diag.c
+++ b/net/netlink/diag.c
@@ -8,41 +8,6 @@
#include "af_netlink.h"
-#ifdef CONFIG_NETLINK_MMAP
-static int sk_diag_put_ring(struct netlink_ring *ring, int nl_type,
- struct sk_buff *nlskb)
-{
- struct netlink_diag_ring ndr;
-
- ndr.ndr_block_size = ring->pg_vec_pages << PAGE_SHIFT;
- ndr.ndr_block_nr = ring->pg_vec_len;
- ndr.ndr_frame_size = ring->frame_size;
- ndr.ndr_frame_nr = ring->frame_max + 1;
-
- return nla_put(nlskb, nl_type, sizeof(ndr), &ndr);
-}
-
-static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- int ret;
-
- mutex_lock(&nlk->pg_vec_lock);
- ret = sk_diag_put_ring(&nlk->rx_ring, NETLINK_DIAG_RX_RING, nlskb);
- if (!ret)
- ret = sk_diag_put_ring(&nlk->tx_ring, NETLINK_DIAG_TX_RING,
- nlskb);
- mutex_unlock(&nlk->pg_vec_lock);
-
- return ret;
-}
-#else
-static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb)
-{
- return 0;
-}
-#endif
-
static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb)
{
struct netlink_sock *nlk = nlk_sk(sk);
@@ -87,10 +52,6 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO))
goto out_nlmsg_trim;
- if ((req->ndiag_show & NDIAG_SHOW_RING_CFG) &&
- sk_diag_put_rings_cfg(sk, skb))
- goto out_nlmsg_trim;
-
nlmsg_end(skb, nlh);
return 0;
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index d1bd4a45ca2d..d26b28def310 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -588,7 +588,7 @@ static int ip_tun_from_nlattr(const struct nlattr *attr,
ipv4 = true;
break;
case OVS_TUNNEL_KEY_ATTR_IPV6_SRC:
- SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst,
+ SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.src,
nla_get_in6_addr(a), is_mask);
ipv6 = true;
break;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index d805cd577a60..3975ac809934 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -3021,7 +3021,7 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr,
int addr_len)
{
struct sock *sk = sock->sk;
- char name[15];
+ char name[sizeof(uaddr->sa_data) + 1];
/*
* Check legality
@@ -3029,7 +3029,11 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr,
if (addr_len != sizeof(struct sockaddr))
return -EINVAL;
- strlcpy(name, uaddr->sa_data, sizeof(name));
+ /* uaddr->sa_data comes from the userspace, it's not guaranteed to be
+ * zero-terminated.
+ */
+ memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data));
+ name[sizeof(uaddr->sa_data)] = 0;
return packet_do_bind(sk, name, 0, pkt_sk(sk)->num);
}
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 06e7c4a37245..694a06f1e0d5 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -820,10 +820,8 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
goto out_module_put;
err = a.ops->walk(skb, &dcb, RTM_DELACTION, &a);
- if (err < 0)
+ if (err <= 0)
goto out_module_put;
- if (err == 0)
- goto noflush_out;
nla_nest_end(skb, nest);
@@ -840,7 +838,6 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
out_module_put:
module_put(a.ops->owner);
err_out:
-noflush_out:
kfree_skb(skb);
return err;
}
diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c
index bb41699c6c49..7ecb14f3db54 100644
--- a/net/sched/act_connmark.c
+++ b/net/sched/act_connmark.c
@@ -109,6 +109,9 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla,
if (ret < 0)
return ret;
+ if (!tb[TCA_CONNMARK_PARMS])
+ return -EINVAL;
+
parm = nla_data(tb[TCA_CONNMARK_PARMS]);
if (!tcf_hash_check(parm->index, a, bind)) {
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 6a0d48525fcf..c36757e72844 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -146,6 +146,7 @@ void unix_notinflight(struct user_struct *user, struct file *fp)
if (s) {
struct unix_sock *u = unix_sk(s);
+ BUG_ON(!atomic_long_read(&u->inflight));
BUG_ON(list_empty(&u->link));
if (atomic_long_dec_and_test(&u->inflight))
@@ -341,6 +342,14 @@ void unix_gc(void)
}
list_del(&cursor);
+ /* Now gc_candidates contains only garbage. Restore original
+ * inflight counters for these as well, and remove the skbuffs
+ * which are creating the cycle(s).
+ */
+ skb_queue_head_init(&hitlist);
+ list_for_each_entry(u, &gc_candidates, link)
+ scan_children(&u->sk, inc_inflight, &hitlist);
+
/* not_cycle_list contains those sockets which do not make up a
* cycle. Restore these to the inflight list.
*/
@@ -350,14 +359,6 @@ void unix_gc(void)
list_move_tail(&u->link, &gc_inflight_list);
}
- /* Now gc_candidates contains only garbage. Restore original
- * inflight counters for these as well, and remove the skbuffs
- * which are creating the cycle(s).
- */
- skb_queue_head_init(&hitlist);
- list_for_each_entry(u, &gc_candidates, link)
- scan_children(&u->sk, inc_inflight, &hitlist);
-
spin_unlock(&unix_gc_lock);
/* Here we are. Hitlist is filled. Die. */
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 449e4a31a3ec..a71f8801edb3 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -54,7 +54,8 @@ country AN: DFS-ETSI
country AR:
(2402 - 2482 @ 40), (36)
- (5170 - 5330 @ 160), (23)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (36), AUTO-BW
(5490 - 5590 @ 80), (36)
(5650 - 5730 @ 80), (36)
(5735 - 5835 @ 80), (36)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 129ea1c2e010..d0a03abbb1c9 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -512,21 +512,17 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
{
int err;
- rtnl_lock();
-
if (!cb->args[0]) {
err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
nl80211_fam.attrbuf, nl80211_fam.maxattr,
nl80211_policy);
if (err)
- goto out_unlock;
+ return err;
*wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk),
nl80211_fam.attrbuf);
- if (IS_ERR(*wdev)) {
- err = PTR_ERR(*wdev);
- goto out_unlock;
- }
+ if (IS_ERR(*wdev))
+ return PTR_ERR(*wdev);
*rdev = wiphy_to_rdev((*wdev)->wiphy);
/* 0 is the first index - add 1 to parse only once */
cb->args[0] = (*rdev)->wiphy_idx + 1;
@@ -536,10 +532,8 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
struct wireless_dev *tmp;
- if (!wiphy) {
- err = -ENODEV;
- goto out_unlock;
- }
+ if (!wiphy)
+ return -ENODEV;
*rdev = wiphy_to_rdev(wiphy);
*wdev = NULL;
@@ -550,21 +544,11 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
}
}
- if (!*wdev) {
- err = -ENODEV;
- goto out_unlock;
- }
+ if (!*wdev)
+ return -ENODEV;
}
return 0;
- out_unlock:
- rtnl_unlock();
- return err;
-}
-
-static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
-{
- rtnl_unlock();
}
/* IE validation */
@@ -4281,9 +4265,10 @@ static int nl80211_dump_station(struct sk_buff *skb,
int sta_idx = cb->args[2];
int err;
+ rtnl_lock();
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
if (err)
- return err;
+ goto out_err;
if (!wdev->netdev) {
err = -EINVAL;
@@ -4319,7 +4304,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
cb->args[2] = sta_idx;
err = skb->len;
out_err:
- nl80211_finish_wdev_dump(rdev);
+ rtnl_unlock();
return err;
}
@@ -5036,9 +5021,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
int path_idx = cb->args[2];
int err;
+ rtnl_lock();
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
if (err)
- return err;
+ goto out_err;
if (!rdev->ops->dump_mpath) {
err = -EOPNOTSUPP;
@@ -5072,7 +5058,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
cb->args[2] = path_idx;
err = skb->len;
out_err:
- nl80211_finish_wdev_dump(rdev);
+ rtnl_unlock();
return err;
}
@@ -5232,9 +5218,10 @@ static int nl80211_dump_mpp(struct sk_buff *skb,
int path_idx = cb->args[2];
int err;
+ rtnl_lock();
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
if (err)
- return err;
+ goto out_err;
if (!rdev->ops->dump_mpp) {
err = -EOPNOTSUPP;
@@ -5267,7 +5254,7 @@ static int nl80211_dump_mpp(struct sk_buff *skb,
cb->args[2] = path_idx;
err = skb->len;
out_err:
- nl80211_finish_wdev_dump(rdev);
+ rtnl_unlock();
return err;
}
@@ -7336,9 +7323,12 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
int start = cb->args[2], idx = 0;
int err;
+ rtnl_lock();
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
- if (err)
+ if (err) {
+ rtnl_unlock();
return err;
+ }
wdev_lock(wdev);
spin_lock_bh(&rdev->bss_lock);
@@ -7361,7 +7351,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
wdev_unlock(wdev);
cb->args[2] = idx;
- nl80211_finish_wdev_dump(rdev);
+ rtnl_unlock();
return skb->len;
}
@@ -7445,9 +7435,10 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
int res;
bool radio_stats;
+ rtnl_lock();
res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
if (res)
- return res;
+ goto out_err;
/* prepare_wdev_dump parsed the attributes */
radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
@@ -7488,7 +7479,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
cb->args[2] = survey_idx;
res = skb->len;
out_err:
- nl80211_finish_wdev_dump(rdev);
+ rtnl_unlock();
return res;
}
@@ -10573,17 +10564,13 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
void *data = NULL;
unsigned int data_len = 0;
- rtnl_lock();
-
if (cb->args[0]) {
/* subtract the 1 again here */
struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
struct wireless_dev *tmp;
- if (!wiphy) {
- err = -ENODEV;
- goto out_unlock;
- }
+ if (!wiphy)
+ return -ENODEV;
*rdev = wiphy_to_rdev(wiphy);
*wdev = NULL;
@@ -10604,13 +10591,11 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
nl80211_fam.attrbuf, nl80211_fam.maxattr,
nl80211_policy);
if (err)
- goto out_unlock;
+ return err;
if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID] ||
- !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) {
- err = -EINVAL;
- goto out_unlock;
- }
+ !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD])
+ return -EINVAL;
*wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk),
nl80211_fam.attrbuf);
@@ -10619,10 +10604,8 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
*rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
nl80211_fam.attrbuf);
- if (IS_ERR(*rdev)) {
- err = PTR_ERR(*rdev);
- goto out_unlock;
- }
+ if (IS_ERR(*rdev))
+ return PTR_ERR(*rdev);
vid = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID]);
subcmd = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]);
@@ -10635,19 +10618,15 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
continue;
- if (!vcmd->dumpit) {
- err = -EOPNOTSUPP;
- goto out_unlock;
- }
+ if (!vcmd->dumpit)
+ return -EOPNOTSUPP;
vcmd_idx = i;
break;
}
- if (vcmd_idx < 0) {
- err = -EOPNOTSUPP;
- goto out_unlock;
- }
+ if (vcmd_idx < 0)
+ return -EOPNOTSUPP;
if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]) {
data = nla_data(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]);
@@ -10664,9 +10643,6 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
/* keep rtnl locked in successful case */
return 0;
- out_unlock:
- rtnl_unlock();
- return err;
}
static int nl80211_vendor_cmd_dump(struct sk_buff *skb,
@@ -10681,9 +10657,10 @@ static int nl80211_vendor_cmd_dump(struct sk_buff *skb,
int err;
struct nlattr *vendor_data;
+ rtnl_lock();
err = nl80211_prepare_vendor_dump(skb, cb, &rdev, &wdev);
if (err)
- return err;
+ goto out;
vcmd_idx = cb->args[2];
data = (void *)cb->args[3];
@@ -10692,18 +10669,26 @@ static int nl80211_vendor_cmd_dump(struct sk_buff *skb,
if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV)) {
- if (!wdev)
- return -EINVAL;
+ if (!wdev) {
+ err = -EINVAL;
+ goto out;
+ }
if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
- !wdev->netdev)
- return -EINVAL;
+ !wdev->netdev) {
+ err = -EINVAL;
+ goto out;
+ }
if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
if (wdev->netdev &&
- !netif_running(wdev->netdev))
- return -ENETDOWN;
- if (!wdev->netdev && !wdev->p2p_started)
- return -ENETDOWN;
+ !netif_running(wdev->netdev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+ if (!wdev->netdev && !wdev->p2p_started) {
+ err = -ENETDOWN;
+ goto out;
+ }
}
}
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 5b4906ad1b81..a6451bf9a717 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -69,7 +69,7 @@ module_param(bss_entries_limit, int, 0644);
MODULE_PARM_DESC(bss_entries_limit,
"limit to number of scan BSS entries (per wiphy, default 1000)");
-#define IEEE80211_SCAN_RESULT_EXPIRE (7 * HZ)
+#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
static void bss_free(struct cfg80211_internal_bss *bss)
{
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index cf0193b74ae3..d5b4ac7bf0d8 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -3027,6 +3027,11 @@ static int __net_init xfrm_net_init(struct net *net)
{
int rv;
+ /* Initialize the per-net locks here */
+ spin_lock_init(&net->xfrm.xfrm_state_lock);
+ rwlock_init(&net->xfrm.xfrm_policy_lock);
+ mutex_init(&net->xfrm.xfrm_cfg_mutex);
+
rv = xfrm_statistics_init(net);
if (rv < 0)
goto out_statistics;
@@ -3043,11 +3048,6 @@ static int __net_init xfrm_net_init(struct net *net)
if (rv < 0)
goto out;
- /* Initialize the per-net locks here */
- spin_lock_init(&net->xfrm.xfrm_state_lock);
- rwlock_init(&net->xfrm.xfrm_policy_lock);
- mutex_init(&net->xfrm.xfrm_cfg_mutex);
-
return 0;
out:
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 805681a7d356..7a5a64e70b4d 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -412,7 +412,14 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es
up = nla_data(rp);
ulen = xfrm_replay_state_esn_len(up);
- if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen)
+ /* Check the overall length and the internal bitmap length to avoid
+ * potential overflow. */
+ if (nla_len(rp) < ulen ||
+ xfrm_replay_state_esn_len(replay_esn) != ulen ||
+ replay_esn->bmp_len != up->bmp_len)
+ return -EINVAL;
+
+ if (up->replay_window > up->bmp_len * sizeof(__u32) * 8)
return -EINVAL;
return 0;
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 58e79e02f217..c67f9c212dd1 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1921,6 +1921,7 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
info.output_pool != client->pool->size)) {
if (snd_seq_write_pool_allocated(client)) {
/* remove all existing cells */
+ snd_seq_pool_mark_closing(client->pool);
snd_seq_queue_client_leave_cells(client->number);
snd_seq_pool_done(client->pool);
}
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 86240d02b530..3f4efcb85df5 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -70,6 +70,9 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)
return;
*fifo = NULL;
+ if (f->pool)
+ snd_seq_pool_mark_closing(f->pool);
+
snd_seq_fifo_clear(f);
/* wake up clients if any */
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index dfa5156f3585..5847c4475bf3 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -414,6 +414,18 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
return 0;
}
+/* refuse the further insertion to the pool */
+void snd_seq_pool_mark_closing(struct snd_seq_pool *pool)
+{
+ unsigned long flags;
+
+ if (snd_BUG_ON(!pool))
+ return;
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->closing = 1;
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
/* remove events */
int snd_seq_pool_done(struct snd_seq_pool *pool)
{
@@ -424,10 +436,6 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
return -EINVAL;
/* wait for closing all threads */
- spin_lock_irqsave(&pool->lock, flags);
- pool->closing = 1;
- spin_unlock_irqrestore(&pool->lock, flags);
-
if (waitqueue_active(&pool->output_sleep))
wake_up(&pool->output_sleep);
@@ -484,6 +492,7 @@ int snd_seq_pool_delete(struct snd_seq_pool **ppool)
*ppool = NULL;
if (pool == NULL)
return 0;
+ snd_seq_pool_mark_closing(pool);
snd_seq_pool_done(pool);
kfree(pool);
return 0;
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
index 4a2ec779b8a7..32f959c17786 100644
--- a/sound/core/seq/seq_memory.h
+++ b/sound/core/seq/seq_memory.h
@@ -84,6 +84,7 @@ static inline int snd_seq_total_cells(struct snd_seq_pool *pool)
int snd_seq_pool_init(struct snd_seq_pool *pool);
/* done pool - free events */
+void snd_seq_pool_mark_closing(struct snd_seq_pool *pool);
int snd_seq_pool_done(struct snd_seq_pool *pool);
/* create pool */
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index ab4cdab5cfa5..79edd88d5cd0 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1905,7 +1905,7 @@ static int hw_card_start(struct hw *hw)
return err;
/* Set DMA transfer mask */
- if (dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) {
+ if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) {
dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits));
} else {
dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index cf0785ddbd14..1d4f34379f56 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6040,6 +6040,8 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
ALC295_STANDARD_PINS,
{0x17, 0x21014040},
{0x18, 0x21a19050}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_STANDARD_PINS),
SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC298_STANDARD_PINS,
{0x17, 0x90170110}),
diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c
index 71a2052f1089..9b1c8c98946c 100644
--- a/sound/soc/codecs/wcd-dsp-mgr.c
+++ b/sound/soc/codecs/wcd-dsp-mgr.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/stringify.h>
#include <linux/of.h>
+#include <linux/debugfs.h>
#include <linux/component.h>
#include <linux/dma-mapping.h>
#include <soc/qcom/ramdump.h>
@@ -181,6 +182,10 @@ struct wdsp_mgr_priv {
struct work_struct ssr_work;
u16 ready_status;
struct completion ready_compl;
+
+ /* Debugfs related */
+ struct dentry *entry;
+ bool panic_on_error;
};
static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type)
@@ -655,6 +660,12 @@ static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp)
goto err_read_dumps;
}
+ /*
+ * If panic_on_error flag is explicitly set through the debugfs,
+ * then cause a BUG here to aid debugging.
+ */
+ BUG_ON(wdsp->panic_on_error);
+
rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr;
rd_seg.size = img_section.size;
rd_seg.v_address = wdsp->dump_data.rd_v_addr;
@@ -948,6 +959,22 @@ static int wdsp_mgr_compare_of(struct device *dev, void *data)
!strcmp(dev_name(dev), cmpnt->cdev_name)));
}
+static void wdsp_mgr_debugfs_init(struct wdsp_mgr_priv *wdsp)
+{
+ wdsp->entry = debugfs_create_dir("wdsp_mgr", NULL);
+ if (IS_ERR_OR_NULL(wdsp->entry))
+ return;
+
+ debugfs_create_bool("panic_on_error", S_IRUGO | S_IWUSR,
+ wdsp->entry, &wdsp->panic_on_error);
+}
+
+static void wdsp_mgr_debugfs_remove(struct wdsp_mgr_priv *wdsp)
+{
+ debugfs_remove_recursive(wdsp->entry);
+ wdsp->entry = NULL;
+}
+
static int wdsp_mgr_bind(struct device *dev)
{
struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
@@ -977,6 +1004,8 @@ static int wdsp_mgr_bind(struct device *dev)
}
}
+ wdsp_mgr_debugfs_init(wdsp);
+
/* Schedule the work to download image if binding was successful. */
if (!ret)
schedule_work(&wdsp->load_fw_work);
@@ -992,6 +1021,8 @@ static void wdsp_mgr_unbind(struct device *dev)
component_unbind_all(dev, wdsp->ops);
+ wdsp_mgr_debugfs_remove(wdsp);
+
if (wdsp->dump_data.rd_dev) {
destroy_ramdump_device(wdsp->dump_data.rd_dev);
wdsp->dump_data.rd_dev = NULL;
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 7f9ad8ebcd3d..44141ae5668e 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -2217,7 +2217,7 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
- init_completion(&mbhc->btn_press_compl);
+ reinit_completion(&mbhc->btn_press_compl);
WCD_MBHC_RSC_UNLOCK(mbhc);
pr_debug("%s: leave\n", __func__);
@@ -2854,6 +2854,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,
}
mutex_init(&mbhc->hphl_pa_lock);
mutex_init(&mbhc->hphr_pa_lock);
+ init_completion(&mbhc->btn_press_compl);
/* Register event notifier */
mbhc->nblock.notifier_call = wcd_event_notify;
diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c
index 3d2fe2c6bed9..fde1d3e815d3 100644
--- a/sound/soc/codecs/wcd-spi.c
+++ b/sound/soc/codecs/wcd-spi.c
@@ -553,7 +553,7 @@ static int wcd_spi_clk_enable(struct spi_device *spi)
{
struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
int ret;
- u32 rd_status;
+ u32 rd_status = 0;
ret = wcd_spi_cmd_nop(spi);
if (IS_ERR_VALUE(ret)) {
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index d533984558f0..7ad3aeaa8fb7 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -192,7 +192,7 @@ module_param(sido_buck_svs_voltage, int,
MODULE_PARM_DESC(sido_buck_svs_voltage,
"setting for SVS voltage for SIDO BUCK");
-#define TASHA_TX_UNMUTE_DELAY_MS 25
+#define TASHA_TX_UNMUTE_DELAY_MS 40
static int tx_unmute_delay = TASHA_TX_UNMUTE_DELAY_MS;
module_param(tx_unmute_delay, int,
@@ -4945,7 +4945,7 @@ static int tasha_codec_enable_spline_src(struct snd_soc_codec *codec,
int src_num,
int event)
{
- u16 src_paired_reg;
+ u16 src_paired_reg = 0;
struct tasha_priv *tasha;
u16 rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
u16 rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL;
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
index e125ed8c2a16..12a4d348d9a9 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -507,7 +507,7 @@ 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);
-#define WCD934X_TX_UNMUTE_DELAY_MS 25
+#define WCD934X_TX_UNMUTE_DELAY_MS 40
static int tx_unmute_delay = WCD934X_TX_UNMUTE_DELAY_MS;
module_param(tx_unmute_delay, int,
@@ -5476,7 +5476,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol,
u32 adc, i, mic_bias_found = 0;
int ret = 0;
char *mad_input;
- bool is_adc2_input = false;
+ bool is_adc_input = false;
tavil_mad_input = ucontrol->value.integer.value[0];
@@ -5524,8 +5524,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol,
snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc);
mad_input_widget = mad_amic_input_widget;
- if (adc == 2)
- is_adc2_input = true;
+ is_adc_input = true;
} else {
/* DMIC type input widget*/
mad_input_widget = tavil_conn_mad_text[tavil_mad_input];
@@ -5533,7 +5532,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol,
dev_dbg(codec->dev,
"%s: tavil input widget = %s, adc_input = %s\n", __func__,
- mad_input_widget, is_adc2_input ? "true" : "false");
+ mad_input_widget, is_adc_input ? "true" : "false");
for (i = 0; i < card->num_of_dapm_routes; i++) {
if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) {
@@ -5578,8 +5577,8 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol,
0x0F, tavil_mad_input);
snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
0x07, mic_bias_found);
- /* for adc2 input, mad should be in micbias mode with BG enabled */
- if (is_adc2_input)
+ /* for all adc inputs, mad should be in micbias mode with BG enabled */
+ if (is_adc_input)
snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
0x88, 0x88);
else
@@ -8581,6 +8580,7 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
{WCD934X_TLMM_DMIC3_CLK_PINCFG, 0xFF, 0x0a},
{WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a},
{WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00},
+ {WCD934X_CPE_SS_CPAR_CFG, 0x10, 0x10},
};
static void tavil_codec_init_reg(struct tavil_priv *priv)
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
index 2082c356203d..3b681f53b17a 100644
--- a/sound/soc/codecs/wcd_cpe_core.c
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -3556,6 +3556,8 @@ static int wcd_cpe_lsm_lab_control(
pr_debug("%s: enter payload_size = %d Enable %d\n",
__func__, pld_size, enable);
+ memset(&cpe_lab_enable, 0, sizeof(cpe_lab_enable));
+
if (fill_lsm_cmd_header_v0_inband(&cpe_lab_enable.hdr, session->id,
(u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
return -EINVAL;
diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c
index c01150f883ff..e1baca358d5f 100644
--- a/sound/soc/msm/apq8096-auto.c
+++ b/sound/soc/msm/apq8096-auto.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/switch.h>
#include <linux/input.h>
+#include <linux/qdsp6v2/apr.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -68,6 +69,7 @@ static int msm_tert_tdm_rx_0_ch = 2; /* ICC STREAM */
static int msm_tert_tdm_rx_1_ch = 2;
static int msm_tert_tdm_rx_2_ch = 2;
static int msm_tert_tdm_rx_3_ch = 2;
+static int msm_tert_tdm_rx_4_ch;
static int msm_tert_tdm_tx_0_ch = 6; /* EC_REF1-EC_REF6(6 CHAN) */
static int msm_tert_tdm_tx_1_ch = 1;
@@ -94,6 +96,7 @@ static int msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
@@ -836,6 +839,24 @@ static int msm_tert_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int msm_tert_tdm_rx_4_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_4_ch = %d\n", __func__,
+ msm_tert_tdm_rx_4_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_4_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_4_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_4_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_4_ch = %d\n", __func__,
+ msm_tert_tdm_rx_4_ch);
+ return 0;
+}
+
static int msm_tert_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1324,6 +1345,40 @@ static int msm_tert_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int msm_tert_tdm_rx_4_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_4_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_4_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_4_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_4_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_4_bit_format);
+ return 0;
+}
+
static int msm_tert_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -2020,6 +2075,11 @@ static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
msm_tert_tdm_rx_3_bit_format);
break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ channels->min = channels->max = msm_tert_tdm_rx_4_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_4_bit_format);
+ break;
case AFE_PORT_ID_TERTIARY_TDM_TX:
channels->min = channels->max = msm_tert_tdm_tx_0_ch;
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
@@ -2600,6 +2660,8 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
msm_tert_tdm_rx_2_ch_get, msm_tert_tdm_rx_2_ch_put),
SOC_ENUM_EXT("TERT_TDM_RX_3 Channels", msm_snd_enum[5],
msm_tert_tdm_rx_3_ch_get, msm_tert_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_4 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_4_ch_get, msm_tert_tdm_rx_4_ch_put),
SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", msm_snd_enum[5],
msm_tert_tdm_tx_0_ch_get, msm_tert_tdm_tx_0_ch_put),
SOC_ENUM_EXT("TERT_TDM_TX_1 Channels", msm_snd_enum[5],
@@ -2648,6 +2710,9 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
SOC_ENUM_EXT("TERT_TDM_RX_3 Bit Format", msm_snd_enum[6],
msm_tert_tdm_rx_3_bit_format_get,
msm_tert_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_4 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_4_bit_format_get,
+ msm_tert_tdm_rx_4_bit_format_put),
SOC_ENUM_EXT("TERT_TDM_TX_0 Bit Format", msm_snd_enum[6],
msm_tert_tdm_tx_0_bit_format_get,
msm_tert_tdm_tx_0_bit_format_put),
@@ -4160,6 +4225,20 @@ static struct snd_soc_dai_link apq8096_auto_be_dai_links[] = {
.ignore_suspend = 1,
},
{
+ .name = LPASS_BE_TERT_TDM_RX_4,
+ .stream_name = "Tertiary TDM4 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36904",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
.name = LPASS_BE_TERT_TDM_TX_0,
.stream_name = "Tertiary TDM0 Capture",
.cpu_dai_name = "msm-dai-q6-tdm.36897",
@@ -4606,12 +4685,21 @@ static int apq8096_asoc_machine_probe(struct platform_device *pdev)
struct snd_soc_card *card;
const struct of_device_id *match;
int ret;
+ enum apr_subsys_state q6_state;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "No platform supplied from device tree\n");
return -EINVAL;
}
+ q6_state = apr_get_q6_state();
+ if (q6_state == APR_SUBSYS_DOWN) {
+ dev_dbg(&pdev->dev, "deferring %s, adsp_state %d\n",
+ __func__, q6_state);
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+
card = populate_snd_card_dailinks(&pdev->dev);
if (!card) {
dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index a7a25ea070da..33443ab151e9 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -113,7 +113,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
- .channels_max = 4,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c
index ec02cdf3ca3c..f6a5d1344568 100644
--- a/sound/soc/msm/msm8998.c
+++ b/sound/soc/msm/msm8998.c
@@ -4155,9 +4155,6 @@ static void update_mi2s_clk_val(int dai_id, int stream)
mi2s_clk[dai_id].clk_freq_in_hz =
mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
}
-
- if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master)
- mi2s_clk[dai_id].clk_freq_in_hz = 0;
}
static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
@@ -4206,6 +4203,13 @@ static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info,
ret = -EINVAL;
goto err;
}
+
+ if (pinctrl_info->pinctrl == NULL) {
+ pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
curr_state = pinctrl_info->curr_state;
pinctrl_info->curr_state = new_state;
pr_debug("%s: curr_state = %s new_state = %s\n", __func__,
@@ -4474,6 +4478,7 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
struct snd_soc_card *card = rtd->card;
struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+ int ret_pinctrl = 0;
dev_dbg(rtd->card->dev,
"%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
@@ -4488,11 +4493,10 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
goto done;
}
if (index == QUAT_MI2S) {
- ret = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE);
- if (ret) {
+ ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE);
+ if (ret_pinctrl) {
pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
- __func__, ret);
- goto done;
+ __func__, ret_pinctrl);
}
}
@@ -4551,6 +4555,7 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_card *card = rtd->card;
struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+ int ret_pinctrl = 0;
pr_debug("%s(): substream = %s stream = %d\n", __func__,
substream->name, substream->stream);
@@ -4571,10 +4576,10 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
mutex_unlock(&mi2s_intf_conf[index].lock);
if (index == QUAT_MI2S) {
- ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE);
- if (ret)
+ ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE);
+ if (ret_pinctrl)
pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
- __func__, ret);
+ __func__, ret_pinctrl);
}
}
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 2929ea0d735b..8fc09130ab7d 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -187,7 +187,7 @@ struct msm_compr_audio {
const u32 compr_codecs[] = {
SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3, SND_AUDIOCODEC_DTS,
- SND_AUDIOCODEC_DSD};
+ SND_AUDIOCODEC_DSD, SND_AUDIOCODEC_TRUEHD};
struct query_audio_effect {
uint32_t mod_id;
@@ -904,7 +904,7 @@ static void populate_codec_list(struct msm_compr_audio *prtd)
COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
prtd->compr_cap.max_fragments =
COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
- prtd->compr_cap.num_codecs = 15;
+ prtd->compr_cap.num_codecs = 16;
prtd->compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
prtd->compr_cap.codecs[2] = SND_AUDIOCODEC_AC3;
@@ -920,6 +920,7 @@ static void populate_codec_list(struct msm_compr_audio *prtd)
prtd->compr_cap.codecs[12] = SND_AUDIOCODEC_DTS;
prtd->compr_cap.codecs[13] = SND_AUDIOCODEC_DSD;
prtd->compr_cap.codecs[14] = SND_AUDIOCODEC_APTX;
+ prtd->compr_cap.codecs[15] = SND_AUDIOCODEC_TRUEHD;
}
static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
@@ -1174,6 +1175,10 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
pr_err("%s: CMD DSD Format block failed ret %d\n",
__func__, ret);
break;
+ case FORMAT_TRUEHD:
+ pr_debug("SND_AUDIOCODEC_TRUEHD\n");
+ /* no media format block needed */
+ break;
case FORMAT_APTX:
pr_debug("SND_AUDIOCODEC_APTX\n");
memset(&aptx_cfg, 0x0, sizeof(struct aptx_dec_bt_addr_cfg));
@@ -1972,6 +1977,12 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream,
break;
}
+ case SND_AUDIOCODEC_TRUEHD: {
+ pr_debug("%s: SND_AUDIOCODEC_TRUEHD\n", __func__);
+ prtd->codec = FORMAT_TRUEHD;
+ break;
+ }
+
case SND_AUDIOCODEC_APTX: {
pr_debug("%s: SND_AUDIOCODEC_APTX\n", __func__);
prtd->codec = FORMAT_APTX;
@@ -2836,20 +2847,14 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream,
SND_AUDIOSTREAMFORMAT_RAW);
break;
case SND_AUDIOCODEC_AC3:
- break;
case SND_AUDIOCODEC_EAC3:
- break;
case SND_AUDIOCODEC_FLAC:
- break;
case SND_AUDIOCODEC_VORBIS:
- break;
case SND_AUDIOCODEC_ALAC:
- break;
case SND_AUDIOCODEC_APE:
- break;
case SND_AUDIOCODEC_DTS:
- break;
case SND_AUDIOCODEC_DSD:
+ case SND_AUDIOCODEC_TRUEHD:
case SND_AUDIOCODEC_APTX:
break;
default:
@@ -3318,6 +3323,7 @@ static int msm_compr_send_dec_params(struct snd_compr_stream *cstream,
switch (prtd->codec) {
case FORMAT_MP3:
case FORMAT_MPEG4_AAC:
+ case FORMAT_TRUEHD:
case FORMAT_APTX:
pr_debug("%s: no runtime parameters for codec: %d\n", __func__,
prtd->codec);
@@ -3385,6 +3391,7 @@ static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol,
case FORMAT_APE:
case FORMAT_DTS:
case FORMAT_DSD:
+ case FORMAT_TRUEHD:
case FORMAT_APTX:
pr_debug("%s: no runtime parameters for codec: %d\n", __func__,
prtd->codec);
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-slim.c b/sound/soc/msm/qdsp6v2/msm-dai-slim.c
index 43a27eb35a44..64ecd7e031f0 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-slim.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-slim.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -314,7 +314,7 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream,
struct msm_slim_dai_data *dai_data = NULL;
struct slim_ch prop;
int rc;
- u8 i;
+ u8 i, j;
dai_data = msm_slim_get_dai_data(drv_data, dai);
if (!dai_data) {
@@ -351,10 +351,6 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream,
}
}
- /* To decrement the channel ref count*/
- for (i = 0; i < dai_data->ch_cnt; i++)
- slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]);
-
prop.prot = SLIM_AUTO_ISO;
prop.baser = SLIM_RATE_4000HZ;
prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
@@ -378,6 +374,8 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream,
error_define_chan:
error_chan_query:
+ for (j = 0; j < i; j++)
+ slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[j]);
return rc;
}
@@ -387,6 +385,7 @@ static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream,
struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
struct msm_slim_dma_data *dma_data = NULL;
struct msm_slim_dai_data *dai_data;
+ int i, rc = 0;
dai_data = msm_slim_get_dai_data(drv_data, dai);
dma_data = snd_soc_dai_get_dma_data(dai, stream);
@@ -405,6 +404,15 @@ static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream,
return;
}
+ for (i = 0; i < dai_data->ch_cnt; i++) {
+ rc = slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]);
+ if (rc) {
+ dev_err(dai->dev,
+ "%s: dealloc_ch failed, err = %d\n",
+ __func__, rc);
+ }
+ }
+
snd_soc_dai_set_dma_data(dai, stream, NULL);
/* clear prepared state for the dai */
CLR_DAI_STATE(dai_data->status, DAI_STATE_PREPARED);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 7ce73484a681..f461f360d88d 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -80,6 +80,7 @@ static uint32_t voc_session_id = ALL_SESSION_VSID;
static int msm_route_ext_ec_ref;
static bool is_custom_stereo_on;
static bool is_ds2_on;
+static bool swap_ch;
enum {
MADNONE,
@@ -7933,6 +7934,9 @@ static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = {
@@ -13325,6 +13329,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIM7_UL_HL", NULL, "HFP_SLIM7_UL_HL"},
{"HFP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"},
{"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"},
+ {"AUX_PCM_RX", NULL, "INTHFP_DL_HL"},
{"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"},
{"MI2S_RX", NULL, "MI2S_DL_HL"},
{"MI2S_UL_HL", NULL, "MI2S_TX"},
@@ -13729,6 +13734,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"AUX_PCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"AUX_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"AUX_PCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"AUX_PCM_RX Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX Port Mixer"},
{"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
@@ -14562,6 +14568,67 @@ static const struct snd_kcontrol_new
},
};
+static int msm_routing_stereo_channel_reverse_control_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = swap_ch;
+ pr_debug("%s: Swap channel value: %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_stereo_channel_reverse_control_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i, idx, be_index, port_id;
+ int ret = 0;
+ unsigned long copp;
+
+ pr_debug("%s Swap channel value:%ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ swap_ch = ucontrol->value.integer.value[0];
+
+ mutex_lock(&routing_lock);
+ for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
+ port_id = msm_bedais[be_index].port_id;
+ if (!msm_bedais[be_index].active)
+ continue;
+
+ for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0],
+ MSM_FRONTEND_DAI_MM_SIZE) {
+ copp = session_copp_map[i][SESSION_TYPE_RX][be_index];
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+ if (!test_bit(idx, &copp))
+ continue;
+
+ pr_debug("%s: swap channel control of portid:%d, coppid:%d\n",
+ __func__, port_id, idx);
+ ret = adm_swap_speaker_channels(
+ port_id, idx,
+ msm_bedais[be_index].sample_rate,
+ swap_ch);
+ if (ret) {
+ pr_err("%s:Swap_channel failed, err=%d\n",
+ __func__, ret);
+ goto done;
+ }
+ }
+ }
+ }
+done:
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+static const struct snd_kcontrol_new stereo_channel_reverse_control[] = {
+ SOC_SINGLE_EXT("Swap channel", SND_SOC_NOPM, 0,
+ 1, 0, msm_routing_stereo_channel_reverse_control_get,
+ msm_routing_stereo_channel_reverse_control_put),
+};
+
static struct snd_pcm_ops msm_routing_pcm_ops = {
.hw_params = msm_pcm_routing_hw_params,
.close = msm_pcm_routing_close,
@@ -14627,6 +14694,8 @@ static int msm_routing_probe(struct snd_soc_platform *platform)
snd_soc_add_platform_controls(platform, aptx_dec_license_controls,
ARRAY_SIZE(aptx_dec_license_controls));
+ snd_soc_add_platform_controls(platform, stereo_channel_reverse_control,
+ ARRAY_SIZE(stereo_channel_reverse_control));
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 2da7e6dc11e7..f026e82a73cc 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -4288,6 +4288,136 @@ end:
return ret;
}
+/**
+ * adm_swap_speaker_channels
+ *
+ * Receives port_id, copp_idx, sample rate, spk_swap and
+ * send MFC command to swap speaker channel.
+ * Return zero on success. On failure returns nonzero.
+ *
+ * port_id - Passed value, port_id for which channels swap is wanted
+ * copp_idx - Passed value, copp_idx for which channels swap is wanted
+ * sample_rate - Passed value, sample rate used by app type config
+ * spk_swap - Passed value, spk_swap for check if swap flag is set
+*/
+int adm_swap_speaker_channels(int port_id, int copp_idx,
+ int sample_rate, bool spk_swap)
+{
+ struct audproc_mfc_output_media_fmt mfc_cfg;
+ uint16_t num_channels;
+ int port_idx;
+ int ret = 0;
+
+ pr_debug("%s: Enter, port_id %d, copp_idx %d\n",
+ __func__, port_id, copp_idx);
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ num_channels = atomic_read(
+ &this_adm.copp.channels[port_idx][copp_idx]);
+ if (num_channels != 2) {
+ pr_debug("%s: Invalid number of channels: %d\n",
+ __func__, num_channels);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memset(&mfc_cfg, 0, sizeof(mfc_cfg));
+ mfc_cfg.params.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mfc_cfg.params.hdr.pkt_size =
+ sizeof(mfc_cfg);
+ mfc_cfg.params.hdr.src_svc = APR_SVC_ADM;
+ mfc_cfg.params.hdr.src_domain = APR_DOMAIN_APPS;
+ mfc_cfg.params.hdr.src_port = port_id;
+ mfc_cfg.params.hdr.dest_svc = APR_SVC_ADM;
+ mfc_cfg.params.hdr.dest_domain = APR_DOMAIN_ADSP;
+ mfc_cfg.params.hdr.dest_port =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ mfc_cfg.params.hdr.token = port_idx << 16 | copp_idx;
+ mfc_cfg.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+ mfc_cfg.params.payload_addr_lsw = 0;
+ mfc_cfg.params.payload_addr_msw = 0;
+ mfc_cfg.params.mem_map_handle = 0;
+ mfc_cfg.params.payload_size = sizeof(mfc_cfg) -
+ sizeof(mfc_cfg.params);
+ mfc_cfg.data.module_id = AUDPROC_MODULE_ID_MFC;
+ mfc_cfg.data.param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT;
+ mfc_cfg.data.param_size = mfc_cfg.params.payload_size -
+ sizeof(mfc_cfg.data);
+ mfc_cfg.data.reserved = 0;
+ mfc_cfg.sampling_rate = sample_rate;
+ mfc_cfg.bits_per_sample =
+ atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]);
+ mfc_cfg.num_channels = num_channels;
+
+ /* Currently applying speaker swap for only 2 channel use case */
+ if (spk_swap) {
+ mfc_cfg.channel_type[0] =
+ (uint16_t) PCM_CHANNEL_FR;
+ mfc_cfg.channel_type[1] =
+ (uint16_t) PCM_CHANNEL_FL;
+ } else {
+ mfc_cfg.channel_type[0] =
+ (uint16_t) PCM_CHANNEL_FL;
+ mfc_cfg.channel_type[1] =
+ (uint16_t) PCM_CHANNEL_FR;
+ }
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+ pr_debug("%s: mfc config: port_idx %d copp_idx %d copp SR %d copp BW %d copp chan %d\n",
+ __func__, port_idx, copp_idx, mfc_cfg.sampling_rate,
+ mfc_cfg.bits_per_sample, mfc_cfg.num_channels);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&mfc_cfg);
+ if (ret < 0) {
+ pr_err("%s: port_id: for[0x%x] failed %d\n",
+ __func__, port_id, ret);
+ goto done;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: mfc_cfg Set params timed out for port_id: for [0x%x]\n",
+ __func__, port_id);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx])));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]));
+ goto done;
+ }
+
+ pr_debug("%s: mfc_cfg Set params returned success", __func__);
+ ret = 0;
+
+done:
+ return ret;
+}
+EXPORT_SYMBOL(adm_swap_speaker_channels);
+
int adm_set_sound_focus(int port_id, int copp_idx,
struct sound_focus_param soundFocusData)
{
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 6616edcd3347..f0a78dc8aee8 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -5193,7 +5193,7 @@ static int afe_sidetone(u16 tx_port_id, u16 rx_port_id, bool enable)
AFE_API_VERSION_LOOPBACK_CONFIG;
cmd_sidetone.cfg_data.dst_port_id = rx_port_id;
cmd_sidetone.cfg_data.routing_mode = LB_MODE_SIDETONE;
- cmd_sidetone.cfg_data.enable = ((enable == 1) ? sidetone_enable : 0);
+ cmd_sidetone.cfg_data.enable = enable;
pr_debug("%s rx(0x%x) tx(0x%x) enable(%d) mid(0x%x) gain(%d) sidetone_enable(%d)\n",
__func__, rx_port_id, tx_port_id,
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 731f439f5286..a9241cfdfec7 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -2782,6 +2782,9 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
case FORMAT_GEN_COMPR:
open.dec_fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED;
break;
+ case FORMAT_TRUEHD:
+ open.dec_fmt_id = ASM_MEDIA_FMT_TRUEHD;
+ break;
default:
pr_err("%s: Invalid format 0x%x\n", __func__, format);
rc = -EINVAL;
diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/sound/soc/msm/qdsp6v2/q6lsm.c
index 525ec1c30f48..11574a874a5a 100644
--- a/sound/soc/msm/qdsp6v2/q6lsm.c
+++ b/sound/soc/msm/qdsp6v2/q6lsm.c
@@ -1243,14 +1243,12 @@ static int q6lsm_send_cal(struct lsm_client *client,
mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
cal_block = cal_utils_get_only_cal_block(
lsm_common.cal_data[LSM_CAL_IDX]);
- if (cal_block == NULL)
- goto unlock;
- if (cal_block->cal_data.size <= 0) {
+ if (!cal_block || cal_block->cal_data.size <= 0) {
pr_debug("%s: No cal to send!\n", __func__);
- rc = -EINVAL;
goto unlock;
}
+
if (cal_block->cal_data.size != client->lsm_cal_size) {
pr_err("%s: Cal size %zd doesn't match lsm cal size %d\n",
__func__, cal_block->cal_data.size,
diff --git a/sound/soc/msm/sdm660-common.c b/sound/soc/msm/sdm660-common.c
index edf5719fbf3e..90b661dff7ec 100644
--- a/sound/soc/msm/sdm660-common.c
+++ b/sound/soc/msm/sdm660-common.c
@@ -2412,9 +2412,6 @@ static void update_mi2s_clk_val(int dai_id, int stream)
mi2s_clk[dai_id].clk_freq_in_hz =
mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
}
-
- if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master)
- mi2s_clk[dai_id].clk_freq_in_hz = 0;
}
static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index d2ac0386d3da..083887ba0346 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -13,7 +13,8 @@ snd-usb-audio-objs := card.o \
pcm.o \
proc.o \
quirks.o \
- stream.o
+ stream.o \
+ badd.o
snd-usbmidi-lib-objs := midi.o
diff --git a/sound/usb/badd.c b/sound/usb/badd.c
new file mode 100644
index 000000000000..cc6c26cbb624
--- /dev/null
+++ b/sound/usb/badd.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
+
+struct uac3_input_terminal_descriptor badd_baif_in_term_desc = {
+ .bLength = UAC3_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = BADD_IN_TERM_ID_BAIF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .wExTerminalDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_input_terminal_descriptor badd_baof_in_term_desc = {
+ .bLength = UAC3_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = BADD_IN_TERM_ID_BAOF,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = 0x00,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000000,
+ .wExTerminalDescrID = 0x0000,
+ .wConnectorsDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_output_terminal_descriptor badd_baif_out_term_desc = {
+ .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = BADD_OUT_TERM_ID_BAIF,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = 0x00, /* No associated terminal */
+ .bSourceID = BADD_FU_ID_BAIF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000000, /* No controls */
+ .wExTerminalDescrID = 0x0000,
+ .wConnectorsDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_output_terminal_descriptor badd_baof_out_term_desc = {
+ .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = BADD_OUT_TERM_ID_BAOF,
+ .bSourceID = BADD_FU_ID_BAOF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .wExTerminalDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+__u8 monoControls[] = {
+ 0x03, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00};
+
+__u8 stereoControls[] = {
+ 0x03, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00
+};
+
+__u8 badd_mu_src_ids[] = {BADD_IN_TERM_ID_BAOF, BADD_FU_ID_BAIOF};
+
+struct uac3_mixer_unit_descriptor badd_baiof_mu_desc = {
+ .bLength = UAC3_DT_MIXER_UNIT_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_MIXER_UNIT_V3,
+ .bUnitID = BADD_MU_ID_BAIOF,
+ .bNrInPins = 0x02,
+ .baSourceID = badd_mu_src_ids,
+ .bmMixerControls = 0x00,
+ .bmControls = 0x00000000,
+ .wMixerDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baif_fu_desc = {
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAIF,
+ .bSourceID = BADD_IN_TERM_ID_BAIF,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baof_fu_desc = {
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAOF,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baiof_fu_desc = {
+ .bLength = 0x0f,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAIOF,
+ .bSourceID = BADD_IN_TERM_ID_BAIF,
+ .bmaControls = monoControls,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_clock_source_descriptor badd_clock_desc = {
+ .bLength = UAC3_DT_CLOCK_SRC_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_CLOCK_SOURCE,
+ .bClockID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000001,
+ .bReferenceTerminal = 0x00,
+ .wClockSourceStr = 0x0000
+};
+
+void *badd_desc_list[] = {
+ &badd_baif_in_term_desc,
+ &badd_baof_in_term_desc,
+ &badd_baiof_mu_desc,
+ &badd_baif_fu_desc,
+ &badd_baof_fu_desc,
+ &badd_baiof_fu_desc,
+ &badd_clock_desc
+};
+
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 7bc935bab369..23ea575f3bcf 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -45,6 +45,7 @@
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/module.h>
+#include <linux/usb/audio-v3.h>
#include <sound/control.h>
#include <sound/core.h>
@@ -281,7 +282,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
struct usb_host_interface *host_iface;
struct usb_interface_descriptor *altsd;
struct usb_interface *usb_iface;
- void *control_header;
int i, protocol;
usb_iface = usb_ifnum_to_if(dev, ctrlif);
@@ -298,16 +298,13 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
return -EINVAL;
}
- control_header = snd_usb_find_csint_desc(host_iface->extra,
- host_iface->extralen,
- NULL, UAC_HEADER);
altsd = get_iface_desc(host_iface);
protocol = altsd->bInterfaceProtocol;
- if (!control_header) {
- dev_err(&dev->dev, "cannot find UAC_HEADER\n");
- return -EINVAL;
- }
+ /*
+ * UAC 1.0 devices use AC HEADER Desc for linking AS interfaces;
+ * UAC 2.0 and 3.0 devices use IAD for linking AS interfaces
+ */
switch (protocol) {
default:
@@ -317,8 +314,17 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
/* fall through */
case UAC_VERSION_1: {
- struct uac1_ac_header_descriptor *h1 = control_header;
+ void *control_header;
+ struct uac1_ac_header_descriptor *h1;
+
+ control_header = snd_usb_find_csint_desc(host_iface->extra,
+ host_iface->extralen, NULL, UAC_HEADER);
+ if (!control_header) {
+ dev_err(&dev->dev, "cannot find UAC_HEADER\n");
+ return -EINVAL;
+ }
+ h1 = control_header;
if (!h1->bInCollection) {
dev_info(&dev->dev, "skipping empty audio interface (v1)\n");
return -EINVAL;
@@ -335,7 +341,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
break;
}
- case UAC_VERSION_2: {
+ case UAC_VERSION_2:
+ case UAC_VERSION_3: {
struct usb_interface_assoc_descriptor *assoc =
usb_iface->intf_assoc;
if (!assoc) {
@@ -354,7 +361,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
}
if (!assoc) {
- dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n");
+ dev_err(&dev->dev, "Audio class V%d interfaces need an interface association\n",
+ protocol);
return -EINVAL;
}
@@ -554,6 +562,15 @@ static int usb_audio_probe(struct usb_interface *intf,
struct usb_host_interface *alts;
int ifnum;
u32 id;
+ struct usb_interface_assoc_descriptor *assoc;
+
+ assoc = intf->intf_assoc;
+ if (assoc && assoc->bFunctionClass == USB_CLASS_AUDIO &&
+ assoc->bFunctionProtocol == UAC_VERSION_3 &&
+ assoc->bFunctionSubClass == FULL_ADC_PROFILE) {
+ dev_info(&dev->dev, "No support for full-fledged ADC 3.0 yet!!\n");
+ return -EINVAL;
+ }
alts = &intf->altsetting[0];
ifnum = get_iface_desc(alts)->bInterfaceNumber;
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 7ccbcaf6a147..2cd09ceba5e9 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -424,6 +424,10 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
case UAC_VERSION_2:
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+
+ /* Clock rate is fixed at 48 kHz for BADD devices */
+ case UAC_VERSION_3:
+ return 0;
}
}
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 789d19ec035d..2cc3b92f1fba 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -20,6 +20,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -69,6 +70,30 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
format <<= 1;
break;
}
+
+ case UAC_VERSION_3: {
+ switch (fp->maxpacksize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16: {
+ sample_width = BIT_RES_16_BIT;
+ sample_bytes = SUBSLOTSIZE_16_BIT;
+ break;
+ }
+
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ sample_width = BIT_RES_24_BIT;
+ sample_bytes = SUBSLOTSIZE_24_BIT;
+ break;
+ }
+ }
+ format = 1 << format;
+ break;
+ }
}
if ((pcm_formats == 0) &&
@@ -366,6 +391,22 @@ err:
return ret;
}
+static int badd_set_audio_rate_v3(struct snd_usb_audio *chip,
+ struct audioformat *fp)
+{
+ unsigned int rate;
+
+ fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL);
+ if (fp->rate_table == NULL)
+ return -ENOMEM;
+
+ fp->nr_rates = 1;
+ rate = BADD_SAMPLING_RATE;
+ fp->rate_min = fp->rate_max = fp->rate_table[0] = rate;
+ fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+ return 0;
+}
+
/*
* parse the format type I and III descriptors
*/
@@ -415,6 +456,9 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
/* fp->channels is already set in this case */
ret = parse_audio_format_rates_v2(chip, fp);
break;
+ case UAC_VERSION_3:
+ ret = badd_set_audio_rate_v3(chip, fp);
+ break;
}
if (fp->channels < 1) {
@@ -502,7 +546,10 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
fmt->bFormatType);
return -ENOTSUPP;
}
- fp->fmt_type = fmt->bFormatType;
+ if (fp->protocol == UAC_VERSION_3)
+ fp->fmt_type = UAC_FORMAT_TYPE_I;
+ else
+ fp->fmt_type = fmt->bFormatType;
if (err < 0)
return err;
#if 1
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 157c0704817c..b9d9d2e99c78 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -50,6 +50,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -184,6 +185,17 @@ static void *find_audio_control_unit(struct mixer_build *state,
/* we just parse the header */
struct uac_feature_unit_descriptor *hdr = NULL;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ int i;
+
+ for (i = 0; i < NUM_BADD_DESCS; i++) {
+ hdr = (void *)badd_desc_list[i];
+ if (hdr->bUnitID == unit)
+ return hdr;
+ }
+
+ return NULL;
+ }
while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr,
USB_DT_CS_INTERFACE)) != NULL) {
if (hdr->bLength >= 4 &&
@@ -717,7 +729,7 @@ static int check_input_term(struct mixer_build *state, int id,
term->channels = d->bNrChannels;
term->chconfig = le16_to_cpu(d->wChannelConfig);
term->name = d->iTerminal;
- } else { /* UAC_VERSION_2 */
+ } else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_input_terminal_descriptor *d = p1;
/* call recursively to verify that the
@@ -734,6 +746,24 @@ static int check_input_term(struct mixer_build *state, int id,
term->channels = d->bNrChannels;
term->chconfig = le32_to_cpu(d->bmChannelConfig);
term->name = d->iTerminal;
+ } else { /* UAC_VERSION_3 */
+ struct uac3_input_terminal_descriptor *d = p1;
+
+ err = check_input_term(state,
+ d->bCSourceID, term);
+ if (err < 0)
+ return err;
+
+ term->id = id;
+ term->type = d->wTerminalType;
+ if (d->wClusterDescrID == CLUSTER_ID_MONO) {
+ term->channels = NUM_CHANNELS_MONO;
+ term->chconfig = BADD_CH_CONFIG_MONO;
+ } else {
+ term->channels = NUM_CHANNELS_STEREO;
+ term->chconfig = BADD_CH_CONFIG_STEREO;
+ }
+ term->name = d->wTerminalDescrStr;
}
return 0;
case UAC_FEATURE_UNIT: {
@@ -751,41 +781,81 @@ static int check_input_term(struct mixer_build *state, int id,
return 0;
}
case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->id = id;
- term->name = uac_selector_unit_iSelector(d);
+ /* UAC3_MIXER_UNIT_V3 */
+ case UAC2_CLOCK_SELECTOR:
+ /* UAC3_CLOCK_SOURCE */ {
+ if (state->mixer->protocol == UAC_VERSION_3
+ && hdr[2] == UAC3_CLOCK_SOURCE) {
+ struct uac3_clock_source_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16;
+ term->id = id;
+ term->name = d->wClockSourceStr;
+ } else if (state->mixer->protocol == UAC_VERSION_3
+ && hdr[2] == UAC3_MIXER_UNIT_V3) {
+ struct uac3_mixer_unit_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16;
+ if (d->wClusterDescrID == CLUSTER_ID_MONO) {
+ term->channels = NUM_CHANNELS_MONO;
+ term->chconfig = BADD_CH_CONFIG_MONO;
+ } else {
+ term->channels = NUM_CHANNELS_STEREO;
+ term->chconfig = BADD_CH_CONFIG_STEREO;
+ }
+ term->name = d->wMixerDescrStr;
+ } else {
+ struct uac_selector_unit_descriptor *d = p1;
+ /* call recursively to retrieve channel info */
+ err = check_input_term(state,
+ d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ /* virtual type */
+ term->type = d->bDescriptorSubtype << 16;
+ term->id = id;
+ term->name = uac_selector_unit_iSelector(d);
+ }
return 0;
}
case UAC1_PROCESSING_UNIT:
case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 */
/* UAC2_EFFECT_UNIT */
+ /* UAC3_FEATURE_UNIT_V3 */
case UAC2_EXTENSION_UNIT_V2: {
- struct uac_processing_unit_descriptor *d = p1;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ struct uac_feature_unit_descriptor *d = p1;
+
+ id = d->bSourceID;
+ } else {
+ struct uac_processing_unit_descriptor *d = p1;
+
+ if (state->mixer->protocol == UAC_VERSION_2 &&
+ hdr[2] == UAC2_EFFECT_UNIT) {
+ /* UAC2/UAC1 unit IDs overlap here in an
+ * uncompatible way. Ignore this unit
+ * for now.
+ */
+ return 0;
+ }
- if (state->mixer->protocol == UAC_VERSION_2 &&
- hdr[2] == UAC2_EFFECT_UNIT) {
- /* UAC2/UAC1 unit IDs overlap here in an
- * uncompatible way. Ignore this unit for now.
- */
+ if (d->bNrInPins) {
+ id = d->baSourceID[0];
+ break; /* continue to parse */
+ }
+ /* virtual type */
+ term->type = d->bDescriptorSubtype << 16;
+ term->channels =
+ uac_processing_unit_bNrChannels(d);
+ term->chconfig =
+ uac_processing_unit_wChannelConfig(
+ d, state->mixer->protocol);
+ term->name = uac_processing_unit_iProcessing(
+ d, state->mixer->protocol);
return 0;
}
-
- if (d->bNrInPins) {
- id = d->baSourceID[0];
- break; /* continue to parse */
- }
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->channels = uac_processing_unit_bNrChannels(d);
- term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
- term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
- return 0;
+ break;
}
case UAC2_CLOCK_SOURCE: {
struct uac_clock_source_descriptor *d = p1;
@@ -1232,12 +1302,18 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
struct usb_feature_control_info *ctl_info;
unsigned int len = 0;
int mapped_name = 0;
- int nameid = uac_feature_unit_iFeature(desc);
+ int nameid;
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;
unsigned int range;
+ if (state->mixer->protocol == UAC_VERSION_3)
+ nameid = ((struct uac3_feature_unit_descriptor *)
+ raw_desc)->wFeatureDescrStr;
+ else
+ nameid = uac_feature_unit_iFeature(desc);
+
control++; /* change from zero-based to 1-based value */
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
@@ -1258,7 +1334,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
ctl_info = &audio_feature_info[control-1];
if (state->mixer->protocol == UAC_VERSION_1)
cval->val_type = ctl_info->type;
- else /* UAC_VERSION_2 */
+ else /* UAC_VERSION_2 or UAC_VERSION_3*/
cval->val_type = ctl_info->type_uac2 >= 0 ?
ctl_info->type_uac2 : ctl_info->type;
@@ -1381,6 +1457,62 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
snd_usb_mixer_add_control(&cval->head, kctl);
}
+static int find_num_channels(struct mixer_build *state, int dir)
+{
+ int num_ch = -EINVAL, num, i, j, wMaxPacketSize;
+ int ctrlif = get_iface_desc(state->mixer->hostif)->bInterfaceNumber;
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev, ctrlif);
+ struct usb_interface_assoc_descriptor *assoc = usb_iface->intf_assoc;
+ struct usb_host_interface *alts;
+
+ for (i = 0; i < assoc->bInterfaceCount; i++) {
+ int intf = assoc->bFirstInterface + i;
+
+ if (intf != ctrlif) {
+ struct usb_interface *iface =
+ usb_ifnum_to_if(state->mixer->chip->dev, intf);
+
+ alts = &iface->altsetting[1];
+ if (dir == USB_DIR_OUT &&
+ get_endpoint(alts, 0)->bEndpointAddress &
+ USB_DIR_IN)
+ continue;
+ if (dir == USB_DIR_IN &&
+ !(get_endpoint(alts, 0)->bEndpointAddress &
+ USB_DIR_IN))
+ continue;
+ num = iface->num_altsetting;
+ for (j = 1; j < num; j++) {
+ num_ch = NUM_CHANNELS_MONO;
+ alts = &iface->altsetting[j];
+ wMaxPacketSize = le16_to_cpu(
+ get_endpoint(alts, 0)->
+ wMaxPacketSize);
+ switch (wMaxPacketSize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ break;
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24:
+ num_ch = NUM_CHANNELS_STEREO;
+ break;
+ }
+ if (num_ch == NUM_CHANNELS_MONO)
+ continue;
+ else
+ break;
+ }
+ }
+ }
+
+ return num_ch;
+}
+
/*
* parse a feature unit
*
@@ -1412,7 +1544,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
unitid);
return -EINVAL;
}
- } else {
+ } else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_feature_unit_descriptor *ftr = _ftr;
csize = 4;
channels = (hdr->bLength - 6) / 4 - 1;
@@ -1423,11 +1555,114 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
unitid);
return -EINVAL;
}
+ } else {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev,
+ get_iface_desc(state->mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
+
+ csize = 4;
+ switch (unitid) {
+ case BADD_FU_ID_BAIOF:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+
+ case BADD_FU_ID_BAOF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADPHONE:
+ case PROF_HEADSET_ADAPTER:
+ channels = NUM_CHANNELS_STEREO;
+ bmaControls = stereoControls;
+ badd_baiof_mu_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ case PROF_SPEAKERPHONE:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baof_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ default:
+ channels = find_num_channels(state,
+ USB_DIR_OUT);
+ if (channels < 0) {
+ usb_audio_err(state->chip,
+ "unit %u: Cant find num of channels\n",
+ unitid);
+ return channels;
+ }
+
+ bmaControls = (channels == NUM_CHANNELS_MONO) ?
+ monoControls : stereoControls;
+ badd_baof_in_term_desc.wClusterDescrID =
+ (channels == NUM_CHANNELS_MONO) ?
+ CLUSTER_ID_MONO : CLUSTER_ID_STEREO;
+ break;
+ }
+ break;
+
+ case BADD_FU_ID_BAIF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ case PROF_SPEAKERPHONE:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ default:
+ channels = find_num_channels(state, USB_DIR_IN);
+ if (channels < 0) {
+ usb_audio_err(state->chip,
+ "unit %u: Cant find num of channels\n",
+ unitid);
+ return channels;
+ }
+
+ bmaControls = (channels == NUM_CHANNELS_MONO) ?
+ monoControls : stereoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ (channels == NUM_CHANNELS_MONO) ?
+ CLUSTER_ID_MONO : CLUSTER_ID_STEREO;
+ break;
+ }
+ break;
+ }
}
/* parse the source unit */
- if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
- return err;
+ if (state->mixer->protocol != UAC_VERSION_3) {
+ err = parse_audio_unit(state, hdr->bSourceID);
+ if (err < 0)
+ return err;
+ } else {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev,
+ get_iface_desc(state->mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
+
+ switch (unitid) {
+ case BADD_FU_ID_BAOF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ hdr->bSourceID = BADD_MU_ID_BAIOF;
+ break;
+ default:
+ hdr->bSourceID = BADD_IN_TERM_ID_BAOF;
+ break;
+ }
+ }
+ err = parse_audio_unit(state, hdr->bSourceID);
+ if (err < 0)
+ return err;
+ }
/* determine the input source type and name */
err = check_input_term(state, hdr->bSourceID, &iterm);
@@ -1481,7 +1716,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
build_feature_ctl(state, _ftr, 0, i, &iterm,
unitid, 0);
}
- } else { /* UAC_VERSION_2 */
+ } else { /* UAC_VERSION_2 or UAC_VERSION_3*/
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
unsigned int ch_bits = 0;
unsigned int ch_read_only = 0;
@@ -1599,12 +1834,19 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
int input_pins, num_ins, num_outs;
int pin, ich, err;
- if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
- !(num_outs = uac_mixer_unit_bNrChannels(desc))) {
- usb_audio_err(state->chip,
- "invalid MIXER UNIT descriptor %d\n",
- unitid);
- return -EINVAL;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ input_pins = badd_baiof_mu_desc.bNrInPins;
+ num_outs =
+ (badd_baiof_mu_desc.wClusterDescrID == CLUSTER_ID_MONO) ?
+ NUM_CHANNELS_MONO : NUM_CHANNELS_STEREO;
+ } else {
+ if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
+ !(num_outs = uac_mixer_unit_bNrChannels(desc))) {
+ usb_audio_err(state->chip,
+ "invalid MIXER UNIT descriptor %d\n",
+ unitid);
+ return -EINVAL;
+ }
}
num_ins = 0;
@@ -1624,9 +1866,14 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
int och, ich_has_controls = 0;
for (och = 0; och < num_outs; och++) {
- __u8 *c = uac_mixer_unit_bmControls(desc,
- state->mixer->protocol);
+ __u8 *c = NULL;
+ if (state->mixer->protocol == UAC_VERSION_3)
+ c =
+ &(badd_baiof_mu_desc.bmMixerControls);
+ else
+ c = uac_mixer_unit_bmControls(desc,
+ state->mixer->protocol);
if (check_matrix_bitmap(c, ich, och, num_outs)) {
ich_has_controls = 1;
break;
@@ -2134,16 +2381,28 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
case UAC_MIXER_UNIT:
return parse_audio_mixer_unit(state, unitid, p1);
case UAC_SELECTOR_UNIT:
+ /* UAC3_MIXER_UNIT_V3 has the same value */
case UAC2_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
+ /* UAC3_CLOCK_SOURCE has the same value */
+ if (state->mixer->protocol == UAC_VERSION_3 &&
+ p1[2] == UAC3_CLOCK_SOURCE)
+ return 0; /* NOP */
+ else if (state->mixer->protocol == UAC_VERSION_3
+ && p1[2] == UAC3_MIXER_UNIT_V3)
+ return parse_audio_mixer_unit(state, unitid, p1);
+ else
+ return parse_audio_selector_unit(state, unitid, p1);
case UAC_FEATURE_UNIT:
return parse_audio_feature_unit(state, unitid, p1);
case UAC1_PROCESSING_UNIT:
/* UAC2_EFFECT_UNIT has the same value */
+ /* UAC3_FEATURE_UNIT_V3 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
return parse_audio_processing_unit(state, unitid, p1);
- else
+ else if (state->mixer->protocol == UAC_VERSION_2)
return 0; /* FIXME - effect units not implemented yet */
+ else
+ return parse_audio_feature_unit(state, unitid, p1);
case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
@@ -2178,6 +2437,23 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
return 0;
}
+static int make_out_term(struct mixer_build state, int wTerminalType)
+{
+ struct uac3_output_terminal_descriptor *desc = NULL;
+
+ if (wTerminalType == UAC_TERMINAL_STREAMING)
+ desc = &badd_baif_out_term_desc;
+ else {
+ desc = &badd_baof_out_term_desc;
+ desc->wTerminalType = wTerminalType;
+ }
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type = desc->wTerminalType;
+ state.oterm.name = desc->wTerminalDescrStr;
+ return parse_audio_unit(&state, desc->bSourceID);
+}
+
/*
* create mixer controls
*
@@ -2186,9 +2462,8 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
{
struct mixer_build state;
- int err;
+ int err = -EINVAL;
const struct usbmix_ctl_map *map;
- void *p;
memset(&state, 0, sizeof(state));
state.chip = mixer->chip;
@@ -2206,44 +2481,108 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
}
}
- p = NULL;
- while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
- mixer->hostif->extralen,
- p, UAC_OUTPUT_TERMINAL)) != NULL) {
- if (mixer->protocol == UAC_VERSION_1) {
- struct uac1_output_terminal_descriptor *desc = p;
-
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
- /* mark terminal ID as visited */
- set_bit(desc->bTerminalID, state.unitbitmap);
- state.oterm.id = desc->bTerminalID;
- state.oterm.type = le16_to_cpu(desc->wTerminalType);
- state.oterm.name = desc->iTerminal;
- err = parse_audio_unit(&state, desc->bSourceID);
+ if (mixer->protocol == UAC_VERSION_3) {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(mixer->chip->dev,
+ get_iface_desc(mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
+
+ switch (assoc->bFunctionSubClass) {
+ case PROF_GENERIC_IO: {
+ if (assoc->bInterfaceCount == 0x02) {
+ if (get_endpoint(mixer->hostif,
+ 0)->bEndpointAddress | USB_DIR_IN)
+ err = make_out_term(state,
+ UAC_TERMINAL_STREAMING);
+ else
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_UNDEFINED);
+ } else {
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_UNDEFINED);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ err = make_out_term(state,
+ UAC_TERMINAL_STREAMING);
+ }
+ break;
+ }
+
+ case PROF_HEADPHONE:
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_HEADPHONES);
+ break;
+ case PROF_SPEAKER:
+ err = make_out_term(state, UAC_OUTPUT_TERMINAL_SPEAKER);
+ break;
+ case PROF_MICROPHONE:
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ err = make_out_term(state, UAC_BIDIR_TERMINAL_HEADSET);
if (err < 0 && err != -EINVAL)
return err;
- } else { /* UAC_VERSION_2 */
- struct uac2_output_terminal_descriptor *desc = p;
-
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
- /* mark terminal ID as visited */
- set_bit(desc->bTerminalID, state.unitbitmap);
- state.oterm.id = desc->bTerminalID;
- state.oterm.type = le16_to_cpu(desc->wTerminalType);
- state.oterm.name = desc->iTerminal;
- err = parse_audio_unit(&state, desc->bSourceID);
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ case PROF_SPEAKERPHONE:
+ err = make_out_term(state,
+ UAC_BIDIR_TERMINAL_SPEAKERPHONE);
if (err < 0 && err != -EINVAL)
return err;
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ }
+ if (err < 0 && err != -EINVAL)
+ return err;
+ } else {
+ void *p;
+
+ p = NULL;
+ while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
+ mixer->hostif->extralen, p,
+ UAC_OUTPUT_TERMINAL)) != NULL) {
+ if (mixer->protocol == UAC_VERSION_1) {
+ struct uac1_output_terminal_descriptor *desc =
+ p;
+
+ if (desc->bLength < sizeof(*desc))
+ continue; /* invalid descriptor? */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type =
+ le16_to_cpu(desc->wTerminalType);
+ state.oterm.name = desc->iTerminal;
+ err = parse_audio_unit(&state, desc->bSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ } else { /* UAC_VERSION_2 */
+ struct uac2_output_terminal_descriptor *desc =
+ p;
+
+ if (desc->bLength < sizeof(*desc))
+ continue; /* invalid descriptor? */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type =
+ le16_to_cpu(desc->wTerminalType);
+ state.oterm.name = desc->iTerminal;
+ err = parse_audio_unit(&state, desc->bSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
- /*
- * For UAC2, use the same approach to also add the
- * clock selectors
- */
- err = parse_audio_unit(&state, desc->bCSourceID);
- if (err < 0 && err != -EINVAL)
- return err;
+ /*
+ * For UAC2, use the same approach to also add
+ * the clock selectors
+ */
+ err = parse_audio_unit(&state,
+ desc->bCSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ }
}
}
@@ -2478,6 +2817,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
case UAC_VERSION_2:
mixer->protocol = UAC_VERSION_2;
break;
+ case UAC_VERSION_3:
+ mixer->protocol = UAC_VERSION_3;
+ break;
}
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 98b2c28ae46b..2bb503576134 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -20,6 +20,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -284,8 +285,6 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
0 /* terminator */
};
struct snd_pcm_chmap_elem *chmap;
- const unsigned int *maps;
- int c;
if (channels > ARRAY_SIZE(chmap->map))
return NULL;
@@ -294,27 +293,42 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
if (!chmap)
return NULL;
- maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
chmap->channels = channels;
- c = 0;
- if (bits) {
- for (; bits && *maps; maps++, bits >>= 1)
- if (bits & 1)
- chmap->map[c++] = *maps;
+ if (protocol == UAC_VERSION_3) {
+ switch (channels) {
+ case 1:
+ chmap->map[0] = SNDRV_CHMAP_MONO;
+ break;
+ case 2:
+ chmap->map[0] = SNDRV_CHMAP_FL;
+ chmap->map[1] = SNDRV_CHMAP_FR;
+ break;
+ }
} else {
- /* If we're missing wChannelConfig, then guess something
- to make sure the channel map is not skipped entirely */
- if (channels == 1)
- chmap->map[c++] = SNDRV_CHMAP_MONO;
- else
- for (; c < channels && *maps; maps++)
- chmap->map[c++] = *maps;
+ int c = 0;
+ const unsigned int *maps =
+ protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
+
+ if (bits) {
+ for (; bits && *maps; maps++, bits >>= 1)
+ if (bits & 1)
+ chmap->map[c++] = *maps;
+ } else {
+ /*
+ * If we're missing wChannelConfig, then guess something
+ * to make sure the channel map is not skipped entirely
+ */
+ if (channels == 1)
+ chmap->map[c++] = SNDRV_CHMAP_MONO;
+ else
+ for (; c < channels && *maps; maps++)
+ chmap->map[c++] = *maps;
+ }
+ for (; c < channels; c++)
+ chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
}
- for (; c < channels; c++)
- chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
-
return chmap;
}
@@ -411,6 +425,9 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
int attributes = 0;
+ if (protocol == UAC_VERSION_3)
+ return 0;
+
csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
/* Creamware Noah has this descriptor after the 2nd endpoint */
@@ -631,6 +648,39 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
iface_no, altno, as->bTerminalLink);
continue;
}
+
+ case UAC_VERSION_3: {
+ int wMaxPacketSize;
+
+ format = UAC_FORMAT_TYPE_I_PCM;
+ clock = BADD_CLOCK_SOURCE;
+ wMaxPacketSize = le16_to_cpu(get_endpoint(alts, 0)
+ ->wMaxPacketSize);
+ switch (wMaxPacketSize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_24: {
+ num_channels = NUM_CHANNELS_MONO;
+ chconfig = BADD_CH_CONFIG_MONO;
+ goto populate_fp;
+ }
+
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ num_channels = NUM_CHANNELS_STEREO;
+ chconfig = BADD_CH_CONFIG_STEREO;
+ goto populate_fp;
+ }
+ default:
+ dev_err(&dev->dev,
+ "%u:%d: invalid wMaxPacketSize\n",
+ iface_no, altno);
+ continue;
+ }
+ }
}
/* get format type */
@@ -664,6 +714,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
fp->maxpacksize * 2)
continue;
+populate_fp:
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
if (! fp) {
dev_err(&dev->dev, "cannot malloc\n");
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index a495a7f6cb22..14ea2239fe11 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -28,6 +28,7 @@
#include <linux/iommu.h>
#include <linux/qcom_iommu.h>
#include <linux/platform_device.h>
+#include <linux/usb/audio-v3.h>
#include "usbaudio.h"
#include "card.h"
@@ -428,12 +429,14 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
protocol = altsd->bInterfaceProtocol;
/* get format type */
- fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
- UAC_FORMAT_TYPE);
- if (!fmt) {
- pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n", __func__,
- subs->interface, subs->altset_idx);
- goto err;
+ if (protocol != UAC_VERSION_3) {
+ fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_FORMAT_TYPE);
+ if (!fmt) {
+ pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n",
+ __func__, subs->interface, subs->altset_idx);
+ goto err;
+ }
}
if (!uadev[card_num].ctrl_intf) {
@@ -441,12 +444,15 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
goto err;
}
- hdr_ptr = snd_usb_find_csint_desc(uadev[card_num].ctrl_intf->extra,
- uadev[card_num].ctrl_intf->extralen,
- NULL, UAC_HEADER);
- if (!hdr_ptr) {
- pr_err("%s: no UAC_HEADER desc\n", __func__);
- goto err;
+ if (protocol != UAC_VERSION_3) {
+ hdr_ptr = snd_usb_find_csint_desc(
+ uadev[card_num].ctrl_intf->extra,
+ uadev[card_num].ctrl_intf->extralen,
+ NULL, UAC_HEADER);
+ if (!hdr_ptr) {
+ pr_err("%s: no UAC_HEADER desc\n", __func__);
+ goto err;
+ }
}
if (protocol == UAC_VERSION_1) {
@@ -474,6 +480,25 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
resp->usb_audio_spec_revision =
((struct uac2_ac_header_descriptor *)hdr_ptr)->bcdADC;
resp->usb_audio_spec_revision_valid = 1;
+ } else if (protocol == UAC_VERSION_3) {
+ switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16: {
+ resp->usb_audio_subslot_size = SUBSLOTSIZE_16_BIT;
+ break;
+ }
+
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ resp->usb_audio_subslot_size = SUBSLOTSIZE_24_BIT;
+ break;
+ }
+ }
+ resp->usb_audio_subslot_size_valid = 1;
} else {
pr_err("%s: unknown protocol version %x\n", __func__, protocol);
goto err;
diff --git a/tools/include/linux/log2.h b/tools/include/linux/log2.h
index 41446668ccce..d5677d39c1e4 100644
--- a/tools/include/linux/log2.h
+++ b/tools/include/linux/log2.h
@@ -13,12 +13,6 @@
#define _TOOLS_LINUX_LOG2_H
/*
- * deal with unrepresentable constant logarithms
- */
-extern __attribute__((const, noreturn))
-int ____ilog2_NaN(void);
-
-/*
* non-constant log of base 2 calculators
* - the arch may override these in asm/bitops.h if they can be implemented
* more efficiently than using fls() and fls64()
@@ -78,7 +72,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n)
#define ilog2(n) \
( \
__builtin_constant_p(n) ? ( \
- (n) < 1 ? ____ilog2_NaN() : \
+ (n) < 2 ? 0 : \
(n) & (1ULL << 63) ? 63 : \
(n) & (1ULL << 62) ? 62 : \
(n) & (1ULL << 61) ? 61 : \
@@ -141,10 +135,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n)
(n) & (1ULL << 4) ? 4 : \
(n) & (1ULL << 3) ? 3 : \
(n) & (1ULL << 2) ? 2 : \
- (n) & (1ULL << 1) ? 1 : \
- (n) & (1ULL << 0) ? 0 : \
- ____ilog2_NaN() \
- ) : \
+ 1 ) : \
(sizeof(n) <= 4) ? \
__ilog2_u32(n) : \
__ilog2_u64(n) \