summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/arm64/tagged-pointers.txt62
-rw-r--r--Documentation/devicetree/bindings/arm/msm/adv7481.txt54
-rw-r--r--Documentation/devicetree/bindings/display/msm/sde.txt26
-rw-r--r--Documentation/devicetree/bindings/drm/msm/mdp.txt42
-rw-r--r--Documentation/devicetree/bindings/fb/adv7533.txt3
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt4
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-ba.txt41
-rw-r--r--Documentation/devicetree/bindings/regulator/max20010.txt77
-rw-r--r--Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt1
-rw-r--r--Makefile2
-rw-r--r--arch/alpha/kernel/osf_sys.c6
-rw-r--r--arch/arm/boot/dts/at91-sama5d3_xplained.dts5
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-ba.dtsi18
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts7
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi67
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi21
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-vidc.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts3
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi26
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-sde.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts11
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-common.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi16
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-regulator.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-vidc.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/sdm660.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts251
-rw-r--r--arch/arm/boot/dts/tegra20-paz00.dts1
-rw-r--r--arch/arm/kvm/psci.c8
-rw-r--r--arch/arm64/configs/msm-auto-perf_defconfig1
-rw-r--r--arch/arm64/configs/msm-auto_defconfig1
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig1
-rw-r--r--arch/arm64/configs/msmcortex_defconfig2
-rw-r--r--arch/arm64/configs/sdm660-perf_defconfig2
-rw-r--r--arch/arm64/configs/sdm660_defconfig3
-rw-r--r--arch/arm64/include/asm/cmpxchg.h2
-rw-r--r--arch/arm64/include/asm/uaccess.h3
-rw-r--r--arch/arm64/kvm/sys_regs.c6
-rw-r--r--arch/metag/include/asm/uaccess.h49
-rw-r--r--arch/powerpc/kernel/exceptions-64e.S12
-rw-r--r--arch/powerpc/kernel/mce.c2
-rw-r--r--arch/powerpc/kernel/traps.c4
-rw-r--r--arch/powerpc/platforms/pseries/dlpar.c1
-rw-r--r--arch/s390/kernel/crash_dump.c15
-rw-r--r--arch/s390/kernel/entry.S21
-rw-r--r--arch/x86/boot/boot.h2
-rw-r--r--arch/x86/include/asm/pmem.h2
-rw-r--r--arch/x86/kernel/fpu/init.c1
-rw-r--r--arch/x86/kvm/x86.c45
-rw-r--r--arch/x86/um/ptrace_64.c2
-rw-r--r--arch/x86/xen/mmu.c7
-rw-r--r--block/blk-integrity.c3
-rw-r--r--crypto/algif_aead.c157
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/base/firmware_class.c1
-rw-r--r--drivers/bluetooth/hci_bcm.c5
-rw-r--r--drivers/bluetooth/hci_intel.c13
-rw-r--r--drivers/char/adsprpc.c8
-rw-r--r--drivers/char/diag/diag_dci.c5
-rw-r--r--drivers/char/diag/diag_masks.c75
-rw-r--r--drivers/char/diag/diag_memorydevice.c8
-rw-r--r--drivers/char/diag/diagchar.h12
-rw-r--r--drivers/char/diag/diagchar_core.c42
-rw-r--r--drivers/char/diag/diagfwd_glink.h2
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c71
-rw-r--r--drivers/char/diag/diagfwd_peripheral.h6
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c4
-rw-r--r--drivers/char/lp.c6
-rw-r--r--drivers/char/mem.c5
-rw-r--r--drivers/char/tpm/tpm_crb.c3
-rw-r--r--drivers/clk/msm/clock-osm.c174
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/clk-regmap-mux-div.c255
-rw-r--r--drivers/clk/qcom/clk-regmap-mux-div.h65
-rw-r--r--drivers/crypto/msm/qcrypto.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v10_0.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v11_0.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v8_0.c29
-rw-r--r--drivers/gpu/drm/drm_edid.c8
-rw-r--r--drivers/gpu/drm/drm_mm.c248
-rw-r--r--drivers/gpu/drm/msm/Kconfig2
-rw-r--r--drivers/gpu/drm/msm/Makefile3
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c7
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.xml.h18
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c82
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h35
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c487
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h36
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c65
-rw-r--r--drivers/gpu/drm/msm/msm_gem_vma.c58
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c56
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h18
-rw-r--r--drivers/gpu/drm/msm/msm_mmu.h4
-rw-r--r--drivers/gpu/drm/msm/msm_rd.c61
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.c1
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.h1
-rw-r--r--drivers/gpu/drm/msm/msm_smmu.c42
-rw-r--r--drivers/gpu/drm/msm/msm_submitqueue.c125
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.c55
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.c123
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.h32
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c75
-rw-r--r--drivers/gpu/drm/msm/sde/sde_plane.c1366
-rw-r--r--drivers/gpu/drm/msm/sde/sde_plane.h6
-rw-r--r--drivers/gpu/drm/msm/sde/sde_rm.c42
-rw-r--r--drivers/gpu/drm/msm/sde/sde_rm.h14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c59
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c2
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c25
-rw-r--r--drivers/gpu/msm/kgsl.c1
-rw-r--r--drivers/gpu/msm/kgsl_device.h5
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c134
-rw-r--r--drivers/iio/dac/ad7303.c6
-rw-r--r--drivers/iio/proximity/as3935.c3
-rw-r--r--drivers/infiniband/core/addr.c4
-rw-r--r--drivers/infiniband/core/sysfs.c2
-rw-r--r--drivers/infiniband/hw/mlx4/main.c1
-rw-r--r--drivers/infiniband/hw/mlx4/mcg.c3
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_fs.c3
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c44
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_vlan.c3
-rw-r--r--drivers/iommu/intel-iommu.c5
-rw-r--r--drivers/md/Kconfig1
-rw-r--r--drivers/md/dm-bufio.c35
-rw-r--r--drivers/md/dm-cache-metadata.c12
-rw-r--r--drivers/md/dm-era-target.c8
-rw-r--r--drivers/md/dm-thin-metadata.c4
-rw-r--r--drivers/md/dm-verity-avb.c34
-rw-r--r--drivers/md/dm.c5
-rw-r--r--drivers/md/persistent-data/dm-btree.c8
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.c15
-rw-r--r--drivers/md/raid5.c6
-rw-r--r--drivers/media/dvb-frontends/cxd2841er.c4
-rw-r--r--drivers/media/i2c/adv7481.c1619
-rw-r--r--drivers/media/i2c/adv7481_reg.h702
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c12
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c8
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c91
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.h5
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c146
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h3
-rw-r--r--drivers/media/platform/msm/vidc/msm_smem.c15
-rw-r--r--drivers/media/platform/msm/vidc/msm_v4l2_vidc.c2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c28
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c6
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c62
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c23
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.h4
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c277
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.h2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_internal.h4
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c1
-rw-r--r--drivers/media/rc/mceusb.c4
-rw-r--r--drivers/media/tuners/tuner-xc2028.c37
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-audio.c42
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c45
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c3
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.c19
-rw-r--r--drivers/media/usb/gspca/konica.c3
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c9
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c8
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c58
-rw-r--r--drivers/misc/qseecom.c24
-rw-r--r--drivers/misc/uid_sys_stats.c107
-rw-r--r--drivers/mmc/core/core.c42
-rw-r--r--drivers/mmc/core/host.c2
-rw-r--r--drivers/mmc/core/sd.c5
-rw-r--r--drivers/mmc/host/sdhci-msm.c20
-rw-r--r--drivers/mmc/host/sdhci-msm.h2
-rw-r--r--drivers/mmc/host/sdhci.c6
-rw-r--r--drivers/net/irda/irda-usb.c2
-rw-r--r--drivers/net/usb/qmi_wwan.c1
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c1
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c20
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c12
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c4
-rw-r--r--drivers/net/wireless/cw1200/sta.c4
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c8
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c9
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c9
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c7
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c122
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h1
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.c28
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.h1
-rw-r--r--drivers/net/wireless/ti/wl18xx/main.c3
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.c5
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.h3
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c6
-rw-r--r--drivers/of/address.c2
-rw-r--r--drivers/pci/host/pci-msm.c13
-rw-r--r--drivers/pci/pci-sysfs.c10
-rw-r--r--drivers/pci/pci.c9
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-v3.h6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c71
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_utils.c5
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c53
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c5
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.c9
-rw-r--r--drivers/platform/msm/mhi/mhi_macros.h3
-rw-r--r--drivers/platform/msm/mhi/mhi_pm.c12
-rw-r--r--drivers/power/supply/qcom/fg-core.h1
-rw-r--r--drivers/power/supply/qcom/fg-util.c41
-rw-r--r--drivers/power/supply/qcom/qpnp-fg-gen3.c36
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/max20010-regulator.c490
-rw-r--r--drivers/regulator/spm-regulator.c106
-rw-r--r--drivers/regulator/tps65023-regulator.c3
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c21
-rw-r--r--drivers/scsi/ufs/ufshcd.c56
-rw-r--r--drivers/soc/qcom/glink.c1
-rw-r--r--drivers/soc/qcom/glink_smem_native_xprt.c2
-rw-r--r--drivers/soc/qcom/icnss.c341
-rw-r--r--drivers/soc/qcom/icnss_utils.c28
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c5
-rwxr-xr-xdrivers/soundwire/soundwire.c71
-rw-r--r--drivers/soundwire/swr-wcd-ctrl.c14
-rw-r--r--drivers/staging/android/lowmemorykiller.c1
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.c13
-rw-r--r--drivers/staging/gdm724x/gdm_mux.c4
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c11
-rw-r--r--drivers/staging/vt6656/usbpipe.c31
-rw-r--r--drivers/target/iscsi/iscsi_target.c1
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c30
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c1
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c10
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.h2
-rw-r--r--drivers/target/target_core_file.c3
-rw-r--r--drivers/target/target_core_sbc.c5
-rw-r--r--drivers/target/target_core_tpg.c152
-rw-r--r--drivers/target/target_core_transport.c4
-rw-r--r--drivers/thermal/msm_thermal.c2
-rw-r--r--drivers/tty/pty.c7
-rw-r--r--drivers/tty/serial/omap-serial.c9
-rw-r--r--drivers/tty/serial/samsung.c9
-rw-r--r--drivers/usb/class/cdc-acm.c13
-rw-r--r--drivers/usb/core/driver.c21
-rw-r--r--drivers/usb/core/file.c9
-rw-r--r--drivers/usb/core/hub.c30
-rw-r--r--drivers/usb/gadget/configfs.c46
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/usb/gadget/function/f_mtp.c4
-rw-r--r--drivers/usb/host/xhci-mem.c4
-rw-r--r--drivers/usb/host/xhci-pci.c7
-rw-r--r--drivers/usb/host/xhci-plat.c2
-rw-r--r--drivers/usb/misc/iowarrior.c2
-rw-r--r--drivers/usb/misc/legousbtower.c38
-rw-r--r--drivers/usb/misc/usbtest.c1
-rw-r--r--drivers/usb/musb/tusb6010_omap.c13
-rw-r--r--drivers/usb/pd/policy_engine.c15
-rw-r--r--drivers/usb/serial/ftdi_sio.c11
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h8
-rw-r--r--drivers/usb/serial/io_ti.c5
-rw-r--r--drivers/usb/serial/mct_u232.c2
-rw-r--r--drivers/usb/serial/option.c8
-rw-r--r--drivers/usb/serial/qcserial.c2
-rw-r--r--drivers/usb/storage/ene_ub6250.c90
-rw-r--r--drivers/uwb/i1480/dfu/usb.c5
-rw-r--r--drivers/vfio/vfio_iommu_type1.c102
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/fbdev/msm/mdss.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c9
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h36
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c25
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c57
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c8
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c7
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c12
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c19
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c8
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c18
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c12
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c11
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.h2
-rw-r--r--drivers/video/fbdev/msm/msm_dba/adv7533.c16
-rw-r--r--drivers/video/msm/ba/Kconfig12
-rw-r--r--drivers/video/msm/ba/Makefile5
-rw-r--r--drivers/video/msm/ba/msm_ba.c892
-rw-r--r--drivers/video/msm/ba/msm_ba_common.c645
-rw-r--r--drivers/video/msm/ba/msm_ba_common.h40
-rw-r--r--drivers/video/msm/ba/msm_ba_debug.c223
-rw-r--r--drivers/video/msm/ba/msm_ba_debug.h84
-rw-r--r--drivers/video/msm/ba/msm_ba_internal.h220
-rw-r--r--drivers/video/msm/ba/msm_v4l2_ba.c615
-rw-r--r--drivers/watchdog/pcwd_usb.c3
-rw-r--r--fs/block_dev.c11
-rw-r--r--fs/ceph/acl.c4
-rw-r--r--fs/ceph/inode.c27
-rw-r--r--fs/ceph/super.h1
-rw-r--r--fs/ceph/xattr.c3
-rw-r--r--fs/cifs/cifs_unicode.c6
-rw-r--r--fs/cifs/cifs_unicode.h5
-rw-r--r--fs/cifs/cifssmb.c3
-rw-r--r--fs/cifs/ioctl.c2
-rw-r--r--fs/cifs/smb2pdu.c14
-rw-r--r--fs/ext4/crypto.c2
-rw-r--r--fs/ext4/crypto_fname.c2
-rw-r--r--fs/ext4/crypto_policy.c66
-rw-r--r--fs/ext4/inode.c5
-rw-r--r--fs/ext4/namei.c4
-rw-r--r--fs/ext4/page-io.c2
-rw-r--r--fs/f2fs/crypto_fname.c2
-rw-r--r--fs/f2fs/crypto_policy.c65
-rw-r--r--fs/f2fs/dir.c32
-rw-r--r--fs/f2fs/f2fs.h3
-rw-r--r--fs/f2fs/hash.c7
-rw-r--r--fs/f2fs/inline.c4
-rw-r--r--fs/nfsd/nfs4xdr.c8
-rw-r--r--fs/proc/generic.c1
-rw-r--r--fs/sdcardfs/dentry.c17
-rw-r--r--fs/sdcardfs/derived_perm.c130
-rw-r--r--fs/sdcardfs/inode.c53
-rw-r--r--fs/sdcardfs/lookup.c5
-rw-r--r--fs/sdcardfs/main.c8
-rw-r--r--fs/sdcardfs/packagelist.c2
-rw-r--r--fs/sdcardfs/sdcardfs.h114
-rw-r--r--fs/sdcardfs/super.c49
-rw-r--r--fs/xattr.c2
-rw-r--r--include/drm/drm_mm.h15
-rw-r--r--include/dt-bindings/regulator/max20010.h20
-rw-r--r--include/linux/kprobes.h4
-rw-r--r--include/linux/mmc/host.h4
-rwxr-xr-xinclude/linux/soundwire/soundwire.h6
-rw-r--r--include/media/msm_ba.h81
-rw-r--r--include/net/mac80211.h4
-rw-r--r--include/soc/qcom/icnss.h11
-rw-r--r--include/soc/qcom/minidump.h5
-rw-r--r--include/sound/q6adm-v2.h18
-rw-r--r--include/target/target_core_fabric.h5
-rw-r--r--include/uapi/drm/msm_drm.h37
-rw-r--r--include/uapi/drm/sde_drm.h42
-rw-r--r--include/uapi/linux/v4l2-controls.h6
-rw-r--r--include/uapi/linux/videodev2.h32
-rw-r--r--kernel/fork.c8
-rw-r--r--kernel/irq/chip.c2
-rw-r--r--kernel/kprobes.c2
-rw-r--r--kernel/padata.c2
-rw-r--r--kernel/pid_namespace.c2
-rw-r--r--kernel/sched/fair.c32
-rw-r--r--kernel/sched/sched.h2
-rw-r--r--kernel/trace/trace_kprobe.c5
-rw-r--r--mm/huge_memory.c12
-rw-r--r--net/bluetooth/hci_sock.c3
-rw-r--r--net/core/dev.c37
-rw-r--r--net/ipx/af_ipx.c5
-rw-r--r--net/mac80211/agg-rx.c7
-rw-r--r--net/mac80211/sta_info.c3
-rw-r--r--net/netfilter/xt_HARDIDLETIMER.c4
-rw-r--r--net/netfilter/xt_IDLETIMER.c2
-rw-r--r--net/wireless/db.txt6
-rw-r--r--security/integrity/ima/ima_appraise.c5
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c93
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h3
-rw-r--r--sound/soc/codecs/wsa881x.c85
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c1
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c714
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c261
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c11
-rw-r--r--tools/testing/selftests/x86/ldt_gdt.c46
387 files changed, 13232 insertions, 3498 deletions
diff --git a/Documentation/arm64/tagged-pointers.txt b/Documentation/arm64/tagged-pointers.txt
index d9995f1f51b3..a25a99e82bb1 100644
--- a/Documentation/arm64/tagged-pointers.txt
+++ b/Documentation/arm64/tagged-pointers.txt
@@ -11,24 +11,56 @@ in AArch64 Linux.
The kernel configures the translation tables so that translations made
via TTBR0 (i.e. userspace mappings) have the top byte (bits 63:56) of
the virtual address ignored by the translation hardware. This frees up
-this byte for application use, with the following caveats:
+this byte for application use.
- (1) The kernel requires that all user addresses passed to EL1
- are tagged with tag 0x00. This means that any syscall
- parameters containing user virtual addresses *must* have
- their top byte cleared before trapping to the kernel.
- (2) Non-zero tags are not preserved when delivering signals.
- This means that signal handlers in applications making use
- of tags cannot rely on the tag information for user virtual
- addresses being maintained for fields inside siginfo_t.
- One exception to this rule is for signals raised in response
- to watchpoint debug exceptions, where the tag information
- will be preserved.
+Passing tagged addresses to the kernel
+--------------------------------------
- (3) Special care should be taken when using tagged pointers,
- since it is likely that C compilers will not hazard two
- virtual addresses differing only in the upper byte.
+All interpretation of userspace memory addresses by the kernel assumes
+an address tag of 0x00.
+
+This includes, but is not limited to, addresses found in:
+
+ - pointer arguments to system calls, including pointers in structures
+ passed to system calls,
+
+ - the stack pointer (sp), e.g. when interpreting it to deliver a
+ signal,
+
+ - the frame pointer (x29) and frame records, e.g. when interpreting
+ them to generate a backtrace or call graph.
+
+Using non-zero address tags in any of these locations may result in an
+error code being returned, a (fatal) signal being raised, or other modes
+of failure.
+
+For these reasons, passing non-zero address tags to the kernel via
+system calls is forbidden, and using a non-zero address tag for sp is
+strongly discouraged.
+
+Programs maintaining a frame pointer and frame records that use non-zero
+address tags may suffer impaired or inaccurate debug and profiling
+visibility.
+
+
+Preserving tags
+---------------
+
+Non-zero tags are not preserved when delivering signals. This means that
+signal handlers in applications making use of tags cannot rely on the
+tag information for user virtual addresses being maintained for fields
+inside siginfo_t. One exception to this rule is for signals raised in
+response to watchpoint debug exceptions, where the tag information will
+be preserved.
The architecture prevents the use of a tagged PC, so the upper byte will
be set to a sign-extension of bit 55 on exception return.
+
+
+Other considerations
+--------------------
+
+Special care should be taken when using tagged pointers, since it is
+likely that C compilers will not hazard two virtual addresses differing
+only in the upper byte.
diff --git a/Documentation/devicetree/bindings/arm/msm/adv7481.txt b/Documentation/devicetree/bindings/arm/msm/adv7481.txt
new file mode 100644
index 000000000000..974c0877ac30
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/adv7481.txt
@@ -0,0 +1,54 @@
+ADV7481 chip driver (VIDEO_ADV7481)
+
+VIDEO_ADV7481 is a kernel platform driver that is used for video decoder
+and dual mode HDMI/MHL receiver.
+
+The devicetree representation of the VIDEO_ADV7481 block should be:
+
+Required properties
+
+- compatible: "qcom,adv7481"
+- reg: The i2c slave address of adv7481 device.
+- qcom,cci-master: The i2c master id to be used for adv7481 driver.
+- gpios: The GPIOs required to be configured for the driver. It should
+ be in the order I2C data line, i2c clock line, reset line,
+ interrupt 1, interrupt 2 and interrupt 3.
+- cam_vdig-supply: Should contain regulator to be used for the digital
+ vdd.
+- cam_vio-supply: Should contain regulator to be used for the IO vdd.
+- cam_vana-supply: Should contain regulator from which analog voltage
+ is supplied.
+- qcom,cam-vreg-name: Should specify array of regulator names required
+ for the device.
+- qcom,cam-vreg-min-voltage: Should specify array of minimum voltage
+ level in uV for the regulators specified in the property
+ "qcom,cam-vreg-name".
+- qcom,cam-vreg-max-voltage: Should specify array of maximum voltage
+ level in uV for the regulators specified in the property
+ "qcom,cam-vreg-name".
+- qcom,cam-vreg-op-mode: Should specify array of current level in uA
+ for the regulators specified in the property "qcom,cam-vreg-name".
+
+Example:
+
+ qcom,adv7481@70 {
+ compatible = "qcom,adv7481";
+ reg = <0x70 0xff>;
+ cam_vdig-supply = <&vph_pwr_vreg>;
+ /* Cameras powered by PMIC: */
+ cam_vio-supply = <&pm8994_lvs1>;
+ cam_vana-supply = <&pm8994_l17>;
+ /* Self-powered cameras: */
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,cci-master = <0>;
+ qcom,slave-addr = <0x70>;
+ gpios = <&tlmm 17 0>, /* I2C SDA */
+ <&tlmm 18 0>, /* I2C SCL */
+ <&pm8994_gpios 4 0>, /* RST */
+ <&pm8994_gpios 5 0>, /* INT1 */
+ <&pm8994_gpios 6 0>, /* INT2 */
+ <&pm8994_gpios 7 0>; /* INT3 */
+ };
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index c9e7d7423d7f..e14acdc6303e 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -264,6 +264,22 @@ Bus Scaling Data:
* Current values of src & dst are defined at
include/linux/msm-bus-board.h
+SMMU Subnodes:
+- smmu_sde_****: Child nodes representing sde smmu virtual
+ devices
+
+Subnode properties:
+- compatible: Compatible names used for smmu devices.
+ names should be:
+ "qcom,smmu_sde_unsec": smmu context bank device
+ for unsecure sde real time domain.
+ "qcom,smmu_sde_sec": smmu context bank device
+ for secure sde real time domain.
+ "qcom,smmu_sde_nrt_unsec": smmu context bank device
+ for unsecure sde non-real time domain.
+ "qcom,smmu_sde_nrt_sec": smmu context bank device
+ for secure sde non-real time domain.
+
Please refer to ../../interrupt-controller/interrupts.txt for a general
description of interrupt bindings.
@@ -469,4 +485,14 @@ Example:
<1 590 0 160000>,
<1 590 0 320000>;
};
+
+ smmu_kms_unsec: qcom,smmu_kms_unsec_cb {
+ compatible = "qcom,smmu_sde_unsec";
+ iommus = <&mmss_smmu 0>;
+ };
+
+ smmu_kms_sec: qcom,smmu_kms_sec_cb {
+ compatible = "qcom,smmu_sde_sec";
+ iommus = <&mmss_smmu 1>;
+ };
};
diff --git a/Documentation/devicetree/bindings/drm/msm/mdp.txt b/Documentation/devicetree/bindings/drm/msm/mdp.txt
new file mode 100644
index 000000000000..3a6db0553fe3
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/msm/mdp.txt
@@ -0,0 +1,42 @@
+Qualcomm Technologies,Inc. Adreno/Snapdragon display controller
+
+Required properties:
+
+Optional properties:
+- qcom,sde-plane-id-map: plane id mapping for virtual plane.
+- qcom,sde-plane-id: each virtual plane mapping node.
+- reg: reg property.
+- qcom,display-type: display type this plane is mapped to. It could be
+ "primary", "secondary" and "tertiary".
+- qcom,plane-name: plane name array for this virtual plane. It could be
+ "rgb0", "rgb1", "rgb2", "rgb3", "vig0", "vig1", "vig2", "vig3", "dma0", "dma1",
+ "dma2", "dma3", "cursor0", "cursor1".
+- qcom,plane-type: virtual plane type. It could be "primary", "overlay",
+ "cursor".
+
+Example:
+
+&mdss_mdp {
+ qcom,sde-plane-id-map {
+ qcom,sde-plane-id@0 {
+ reg = <0x0>;
+ qcom,display-type = "primary";
+ qcom,plane-name = "rgb0", "rgb1";
+ qcom,plane-type = "primary";
+ };
+
+ qcom,sde-plane-id@1 {
+ reg = <0x1>;
+ qcom,display-type = "primary";
+ qcom,plane-name = "vig0", "vig1";
+ qcom,plane-type = "overlay";
+ };
+
+ qcom,sde-plane-id@2 {
+ reg = <0x2>;
+ qcom,display-type = "primary";
+ qcom,plane-name = "cursor0", "cursor1";
+ qcom,plane-type = "cursor";
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/fb/adv7533.txt b/Documentation/devicetree/bindings/fb/adv7533.txt
index 8b85fcd730cb..b198f37f8fc6 100644
--- a/Documentation/devicetree/bindings/fb/adv7533.txt
+++ b/Documentation/devicetree/bindings/fb/adv7533.txt
@@ -23,6 +23,8 @@ Optional properties:
- qcom,max-voltage-level Maximum voltage level to be supplied to bridge chip
- qcom,enable-load Load current to bridge chip when enabled
- qcom,disable-load Load current to bridge chip when disabled
+- qcom,post-on-sleep Sleep time (ms) to indicate the sleep
+ time after the vreg is enabled
Example:
&soc {
@@ -46,6 +48,7 @@ Example:
qcom,max-voltage-level = <0>;
qcom,enable-load = <0>;
qcom,disable-load = <0>;
+ qcom,post-on-sleep = <10>;
};
};
};
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 1f8458cd0659..cc55f6e2bfa0 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -187,6 +187,10 @@ Optional properties:
"bl_ctrl_wled" = Backlight controlled by WLED.
"bl_ctrl_dcs" = Backlight controlled by DCS commands.
other: Unknown backlight control. (default)
+- qcom,mdss-dsi-bl-dcs-command-state: A string that specifies the ctrl state for sending brightness
+ controlling commands, this is only available when backlight is controlled by DCS commands.
+ "dsi_lp_mode" = DSI low power mode (default).
+ "dsi_hs_mode" = DSI high speed mode.
- qcom,mdss-dsi-bl-pwm-pmi: Boolean to indicate that PWM control is through second pmic chip.
- qcom,mdss-dsi-bl-pmic-bank-select: LPG channel for backlight.
Requred if blpmiccontroltype is PWM
diff --git a/Documentation/devicetree/bindings/media/video/msm-ba.txt b/Documentation/devicetree/bindings/media/video/msm-ba.txt
new file mode 100644
index 000000000000..9a6fe4d7e8ae
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-ba.txt
@@ -0,0 +1,41 @@
+* Qualcomm Technologies Inc MSM BA
+
+[Root level node]
+==================
+Required properties:
+- compatible: Must be "qcom,msm-ba".
+
+[Subnode]
+==========
+- qcom,ba-input-profile-#: Defines child nodes for the profiles supported
+ by BA driver. Each profile should have properties "qcom,type",
+ "qcom,name", "qcom,ba-input", "qcom,ba-output", "qcom,sd-name",
+ "qcom,ba-node" and "qcom,user-type".
+Required properties:
+- qcom,type: Input type such as CVBS(0), HDMI(4) etc as defined in BA driver.
+ This property is of type u32.
+- qcom,name: Name of the input type. This property is of type string.
+- qcom,ba-input: BA input id supported by a bridge chip for this profile.
+ This property is of type u32.
+- qcom,ba-output: BA output id for the profile. This property is of type u32.
+- qcom,sd-name: Name of the sub-device driver associated with this profile.
+ This property is of type string.
+- qcom,ba-node: Defines the ba node id. This is the avdevice node used by camera
+ for this profile. This property is of type u32.
+- qcom,user-type: This property defines how the profile is being used. If this
+ profile is used by kernel it is set to 0 and if used by userspace
+ it is set to 1. This property is of type u32.
+Example:
+
+ qcom,msm-ba {
+ compatible = "qcom,msm-ba";
+ qcom,ba-input-profile-0 {
+ qcom,type = <4>; /* input type */
+ qcom,name = "HDMI-1"; /* input name */
+ qcom,ba-input = <13>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <0>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/max20010.txt b/Documentation/devicetree/bindings/regulator/max20010.txt
new file mode 100644
index 000000000000..3dd8f6d1cf19
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/max20010.txt
@@ -0,0 +1,77 @@
+Binding for Maxim MAX20010 regulator
+
+MAX20010 is a synchronous step-down converter. It is able to deliver upto 6A
+with 2 different programmable output voltages from 0.5V to 1.27V in 10mV steps
+and from 0.625V to 1.5875V in 12.5mV steps. It supports synchronous
+rectification and automatic PWM/PFM transitions.
+
+The MAX20010 interface is via I2C bus.
+
+=======================
+Supported Properties
+=======================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: should be "maxim,max20010".
+
+- reg
+ Usage: required
+ Value type: <u32>
+ Definition: The device 8-bit I2C address.
+
+- vin-supply
+ Usage: optional
+ Value type: <phandle>
+ Definition: This is the phandle for the parent regulator. Typically used
+ for EN pin control of the buck.
+
+- regulator-initial-mode
+ Usage: optional
+ Value type: <u32>
+ Definition: The regulator operating mode. Should be either
+ "MAX20010_OPMODE_SYNC" or "MAX20010_OPMODE_FPWM".
+ These constants are defined in file
+ include/dt-bindings/regulator/max20010.h
+
+- maxim,vrange-sel
+ Usage: optional
+ Value type: <u32>
+ Definition: Integer value specifies the voltage range to be used.
+ Supported values are 0 or 1.
+ Value 0 supports voltage range from 0.5V to 1.27V in 10mV
+ steps. Value 1 supports voltage range from 0.625V to 1.5875V
+ in 12.5mV steps.
+
+- maxim,soft-start-slew-rate
+ Usage: optional
+ Value type: <u32>
+ Definition: An integer value specifies the slew rate in uV/uS to be used
+ for soft-start operation of the buck. Supported values are
+ 5500, 11000, 22000 and 44000.
+
+- maxim,dvs-slew-rate
+ Usage: optional
+ Value type: <u32>
+ Definition: An integer value specifies the slew rate in uV/uS to be used
+ for buck dynamic voltage scaling operations. Supported
+ values are 5500, 11000, 22000 and 44000.
+
+=======
+Example
+=======
+
+ i2c_0 {
+ max20010-regulator@74 {
+ compatible = "maxim,max20010";
+ reg = <0x74>;
+ vin-supply = <&parent_reg>;
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <1270000>;
+ regulator-initial-mode = <MAX20010_OPMODE_SYNC>;
+ maxim,vrange-sel = <0>;
+ maxim,soft-start-slew-rate = <5500>;
+ maxim,dvs-slew-rate = <5500>;
+ }
+ }
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 8b99dbce871b..9e6dd4905ca9 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -58,6 +58,7 @@ Optional properties:
- pinctrl-names, pinctrl-0, pinctrl-1,.. pinctrl-n: Refer to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
for these optional properties
+- non-removable : defines if the connected ufs device is not removable
Note: If above properties are not defined it can be assumed that the supply
diff --git a/Makefile b/Makefile
index a3ac228e0b3a..1a49c8e64768 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 4
-SUBLEVEL = 68
+SUBLEVEL = 70
EXTRAVERSION =
NAME = Blurry Fish Butt
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 6cc08166ff00..63f06a2b1f7f 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -1188,8 +1188,10 @@ SYSCALL_DEFINE4(osf_wait4, pid_t, pid, int __user *, ustatus, int, options,
if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur)))
return -EFAULT;
- err = 0;
- err |= put_user(status, ustatus);
+ err = put_user(status, ustatus);
+ if (ret < 0)
+ return err ? err : ret;
+
err |= __put_user(r.ru_utime.tv_sec, &ur->ru_utime.tv_sec);
err |= __put_user(r.ru_utime.tv_usec, &ur->ru_utime.tv_usec);
err |= __put_user(r.ru_stime.tv_sec, &ur->ru_stime.tv_sec);
diff --git a/arch/arm/boot/dts/at91-sama5d3_xplained.dts b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
index f3e2b96c06a3..0bd325c314e1 100644
--- a/arch/arm/boot/dts/at91-sama5d3_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
@@ -162,9 +162,10 @@
};
adc0: adc@f8018000 {
+ atmel,adc-vref = <3300>;
+ atmel,adc-channels-used = <0xfe>;
pinctrl-0 = <
&pinctrl_adc0_adtrg
- &pinctrl_adc0_ad0
&pinctrl_adc0_ad1
&pinctrl_adc0_ad2
&pinctrl_adc0_ad3
@@ -172,8 +173,6 @@
&pinctrl_adc0_ad5
&pinctrl_adc0_ad6
&pinctrl_adc0_ad7
- &pinctrl_adc0_ad8
- &pinctrl_adc0_ad9
>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/qcom/apq8096-ba.dtsi b/arch/arm/boot/dts/qcom/apq8096-ba.dtsi
new file mode 100644
index 000000000000..e6524593e502
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apq8096-ba.dtsi
@@ -0,0 +1,18 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ msm_ba: qcom,ba {
+ compatible = "qcom,msm-ba";
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
index bfc6f210a0bb..e731c7edd518 100644
--- a/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
@@ -12,6 +12,7 @@
#include "msm8996-pinctrl.dtsi"
#include "apq8096-camera-sensor-dragonboard.dtsi"
+#include "apq8096-ba.dtsi"
/ {
bluetooth: bt_qca6174 {
diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
index 9c4ff9f184e7..fee184663336 100644
--- a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
@@ -42,15 +42,16 @@
i2c@75b6000 { /* BLSP8 */
/* ADV7533 HDMI Bridge Chip removed on ADP Lite */
- adv7533@3d {
- status = "disabled";
- };
adv7533@39 {
status = "disabled";
};
};
};
+&dsi_adv_7533_2 {
+ /delete-property/ qcom,dsi-display-active;
+};
+
&pil_modem {
pinctrl-names = "default";
pinctrl-0 = <&modem_mux>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi
index 6d91e72851ec..5aa2e1ee8316 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi
@@ -51,7 +51,6 @@
39 01 00 00 78 00 03 f0 a5 a5
39 01 00 00 00 00 02 35 00
39 01 00 00 00 00 02 53 20
- 39 01 00 00 00 00 02 51 60
05 01 00 00 05 00 02 29 00];
qcom,mdss-dsi-off-command = [05 01 00 00 3c 00 02 28 00
05 01 00 00 b4 00 02 10 00];
@@ -136,6 +135,7 @@
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-lp11-init;
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-bl-dcs-command-state = "dsi_hs_mode";
qcom,mdss-dsi-bl-min-level = <1>;
qcom,mdss-dsi-bl-max-level = <255>;
qcom,mdss-pan-physical-width-dimension = <68>;
diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
index a600008341c2..3c9b8e445ff3 100644
--- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
@@ -1098,17 +1098,58 @@
pinctrl-0 = <&quat_tdm_dout_active>;
pinctrl-1 = <&quat_tdm_dout_sleep>;
};
+
+ qcom,adv7481@70 {
+ compatible = "qcom,adv7481";
+ reg = <0x70 0xff>;
+ cam_vdig-supply = <&pm8994_s3>;
+ /* Cameras powered by PMIC: */
+ cam_vio-supply = <&pm8994_lvs1>;
+ cam_vana-supply = <&pm8994_l17>;
+ /* Self-powered cameras: */
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+
+ qcom,cci-master = <0>;
+ gpios = <&tlmm 17 0>, /* I2C SDA */
+ <&tlmm 18 0>, /* I2C SCL */
+ <&pm8994_gpios 4 0>, /* RST */
+ <&pm8994_gpios 5 0>, /* INT1 */
+ <&pm8994_gpios 6 0>, /* INT2 */
+ <&pm8994_gpios 7 0>; /* INT3 */
+ };
+
+ qcom,msm-ba {
+ compatible = "qcom,msm-ba";
+ qcom,ba-input-profile-0 {
+ qcom,type = <4>; /* input type */
+ qcom,name = "HDMI-1"; /* input name */
+ qcom,ba-input = <13>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <0>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+
+ qcom,ba-input-profile-1 {
+ qcom,type = <0>; /* input type */
+ qcom,name = "CVBS-0"; /* input name */
+ qcom,ba-input = <0>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <1>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+ };
};
&pm8994_gpios {
- gpio@c600 { /* GPIO 7 - NFC DWL REQ */
- qcom,mode = <1>;
- qcom,output-type = <0>;
- qcom,pull = <5>;
+ gpio@c600 { /* GPIO 7 - adv7481 INT3 */
+ qcom,mode = <0>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
qcom,src-sel = <0>;
- qcom,master-en = <1>;
status = "okay";
};
@@ -1159,17 +1200,23 @@
status = "okay";
};
- gpio@c300 { /* GPIO 4 */
- qcom,mode = <0>;
+ gpio@c300 { /* GPIO 4 - adv7481 RST */
+ qcom,mode = <1>;
qcom,pull = <0>;
qcom,vin-sel = <2>;
qcom,src-sel = <0>;
status = "okay";
};
- gpio@c400 { /* GPIO 5 */
+ gpio@c400 { /* GPIO 5 - adv7481 INT1 */
+ qcom,mode = <0>;
+ qcom,vin-sel = <2>;
+ qcom,src-sel = <0>;
+ status = "okay";
+ };
+
+ gpio@c500 { /* GPIO 6 - adv7481 INT2*/
qcom,mode = <0>;
- qcom,pull = <0>;
qcom,vin-sel = <2>;
qcom,src-sel = <0>;
status = "okay";
diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
index 7c07102a1fed..797b8a8d8e82 100644
--- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
@@ -868,11 +868,12 @@
asoc-codec-names = "msm-stub-codec.1";
};
- usb_detect {
+ usb_detect: usb_detect {
compatible = "qcom,gpio-usbdetect";
+ qcom,vbus-det-gpio = <&pm8994_gpios 17 0>;
interrupt-parent = <&spmi_bus>;
- interrupts = <0x0 0xd0 0x0>; /* PM8994 GPIO17 */
- interrupt-names = "vbus_det_irq";
+ interrupts = <0x0 0x9 0x0 IRQ_TYPE_NONE>;
+ interrupt-names ="pmic_id_irq";
};
loopback1: qcom,msm-pcm-loopback-low-latency {
@@ -1071,18 +1072,10 @@
};
&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1 2 3>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 0 347 0
- 0x0 1 &intc 0 0 243 0
- 0x0 2 &intc 0 0 180 0
- 0x0 3 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "ss_phy_irq", "pwr_event_irq",
- "pmic_id_irq";
-
+ extcon = <&usb_detect>;
vbus_dwc3-supply = <&usb_otg_switch>;
+ vdda33-supply = <&pm8994_l24>;
+ vdda18-supply = <&pm8994_l12>;
};
&blsp1_uart2 {
diff --git a/arch/arm/boot/dts/qcom/msm8996-vidc.dtsi b/arch/arm/boot/dts/qcom/msm8996-vidc.dtsi
index 5ac31e3dd0cb..21aa1db446e2 100644
--- a/arch/arm/boot/dts/qcom/msm8996-vidc.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-vidc.dtsi
@@ -226,6 +226,7 @@
clocks = <&clock_mmss clk_vmem_ahb_clk>,
<&clock_mmss clk_vmem_maxi_clk>;
clock-names = "ahb", "maxi";
+ clock-config = <0x0 0x0 0x0 0x1>;
qcom,msm-bus,name = "vmem";
qcom,msm-bus,num-cases = <2>;
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index ee069f5d8bdf..7c3f035a841b 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -1737,6 +1737,7 @@
mhi: qcom,mhi {
compatible = "qcom,mhi";
+ status = "disabled";
};
qcom,ipc-spinlock@740000 {
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
index 668cb2844363..1cf61486c9e8 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
@@ -42,9 +42,6 @@
i2c@75b6000 { /* BLSP8 */
/* ADV7533 HDMI Bridge Chip removed on ADP Lite */
- adv7533@3d {
- status = "disabled";
- };
adv7533@39 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
index 1ab8ee9cd538..7f6f3d5d4a4c 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
@@ -46,6 +46,11 @@
qcom,hotplug-temp-hysteresis = <25>;
qcom,therm-reset-temp = <119>;
};
+
+ qcom,adv7481@70 {
+ qcom,cam-vreg-min-voltage = <1300000 0 1800000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 1800000>;
+ };
};
&pil_modem {
diff --git a/arch/arm/boot/dts/qcom/msm8996pro.dtsi b/arch/arm/boot/dts/qcom/msm8996pro.dtsi
index ca89a517df5c..252940c9c3e5 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996pro.dtsi
@@ -1331,6 +1331,10 @@
qcom,poll-ms = <50>;
qcom,limit-temp = <80>;
qcom,core-limit-temp = <90>;
+ msm_thermal_freq: qcom,vdd-apps-rstr {
+ qcom,max-freq-level = <1209600>;
+ qcom,levels = <1056000 1516800 1516800>;
+ };
qcom,vdd-gfx-rstr{
qcom,levels = <6 8 9>; /* Nominal, Turbo, Turbo_L1 */
};
diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
index b53ff87d5092..93b6a7664ed8 100644
--- a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
@@ -83,25 +83,39 @@
};
&dsi_dual_nt35597_video {
- qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07 05 03 04 00];
- qcom,mdss-dsi-t-clk-post = <0x0d>;
- qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-panel-timings = [00 1a 04 06 0a 0a 05 06 05 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x07>;
+ qcom,mdss-dsi-t-clk-pre = <0x25>;
+ qcom,mdss-dsi-tx-eot-append;
qcom,cmd-sync-wait-broadcast;
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
qcom,mdss-dsi-min-refresh-rate = <55>;
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";
+ 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_cmd {
qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07 05 03 04 00];
qcom,mdss-dsi-t-clk-post = <0x0d>;
qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-tx-eot-append;
qcom,cmd-sync-wait-broadcast;
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_truly_video {
@@ -120,6 +134,7 @@
qcom,mdss-dsi-panel-timings = [00 11 04 04 07 0c 04 04 03 03 04 00];
qcom,mdss-dsi-t-clk-post = <0x05>;
qcom,mdss-dsi-t-clk-pre = <0x1b>;
+ qcom,mdss-dsi-tx-eot-append;
qcom,esd-check-enabled;
qcom,mdss-dsi-panel-status-check-mode = "reg_read";
qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
@@ -138,6 +153,7 @@
qcom,mdss-dsi-panel-timings = [00 11 04 04 07 0c 04 04 03 03 04 00];
qcom,mdss-dsi-t-clk-post = <0x05>;
qcom,mdss-dsi-t-clk-pre = <0x1b>;
+ qcom,mdss-dsi-tx-eot-append;
qcom,esd-check-enabled;
qcom,mdss-dsi-panel-status-check-mode = "reg_read";
qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
index 795635d8d13d..354ac830e0fa 100644
--- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
@@ -139,10 +139,15 @@
};
smmu_kms_unsec: qcom,smmu_kms_unsec_cb {
- compatible = "qcom,smmu_mdp_unsec";
+ compatible = "qcom,smmu_sde_unsec";
iommus = <&mmss_smmu 0>;
};
+ smmu_kms_sec: qcom,smmu_kms_sec_cb {
+ compatible = "qcom,smmu_sde_sec";
+ iommus = <&mmss_smmu 1>;
+ };
+
/* data and reg bus scale settings */
qcom,sde-data-bus {
qcom,msm-bus,name = "mdss_sde";
diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi
index 31ad9ecf8c0a..fc546512992d 100644
--- a/arch/arm/boot/dts/qcom/msm8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998.dtsi
@@ -1544,6 +1544,7 @@
qcom,xprt-linkid = <1>;
qcom,xprt-version = <1>;
qcom,fragmented-data;
+ qcom,dynamic-wakeup-source;
};
qcom,spcom {
diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts
index 7be428693f83..7fb0c9d03825 100644
--- a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts
+++ b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts
@@ -208,3 +208,14 @@
"SpkrLeft IN", "SPK1 OUT";
qcom,msm-mbhc-hphl-swh = <0>;
};
+
+&usb2s {
+ status = "okay";
+};
+
+&qusb_phy0 {
+ reg = <0x0c012000 0x180>,
+ <0x00188018 0x4>;
+ reg-names = "qusb_phy_base",
+ "ref_clk_addr";
+};
diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
index baced7758c9f..99766dbcdfe5 100644
--- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
@@ -267,13 +267,19 @@
qusb_phy0: qusb@c012000 {
compatible = "qcom,qusb2phy";
reg = <0x0c012000 0x180>,
+ <0x01fcb24c 0x4>,
+ <0x00780240 0x4>,
<0x00188018 0x4>;
reg-names = "qusb_phy_base",
+ "tcsr_clamp_dig_n_1p8",
+ "tune2_efuse_addr",
"ref_clk_addr";
vdd-supply = <&pm660l_l1>;
vdda18-supply = <&pm660_l10>;
vdda33-supply = <&pm660l_l7>;
qcom,vdd-voltage-level = <0 925000 925000>;
+ qcom,tune2-efuse-bit-pos = <25>;
+ qcom,tune2-efuse-num-bits = <4>;
qcom,qusb-phy-init-seq = <0xf8 0x80
0xb3 0x84
0x83 0x88
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
index 19862f02aa84..3ffd43bcda60 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
@@ -133,7 +133,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>;
qcom,mdss-dsi-min-refresh-rate = <53>;
qcom,mdss-dsi-max-refresh-rate = <60>;
qcom,mdss-dsi-pan-enable-dynamic-fps;
@@ -190,7 +196,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_nt35597_truly_dsc_cmd {
diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
index b701ecd562cd..66bea3050586 100644
--- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
@@ -453,6 +453,8 @@
pm660l_bob: regulator-bob {
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3600000>;
+ qcom,pwm-threshold-current = <2000000>;
+ qcom,init-bob-mode = <2>;
status = "okay";
};
@@ -462,6 +464,8 @@
qcom,set = <3>;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3600000>;
+ qcom,pwm-threshold-current = <2000000>;
+ qcom,init-bob-mode = <2>;
qcom,use-pin-ctrl-voltage1;
};
@@ -471,6 +475,8 @@
qcom,set = <3>;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3600000>;
+ qcom,pwm-threshold-current = <2000000>;
+ qcom,init-bob-mode = <2>;
qcom,use-pin-ctrl-voltage2;
};
@@ -480,6 +486,8 @@
qcom,set = <3>;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3600000>;
+ qcom,pwm-threshold-current = <2000000>;
+ qcom,init-bob-mode = <2>;
qcom,use-pin-ctrl-voltage3;
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-vidc.dtsi b/arch/arm/boot/dts/qcom/sdm660-vidc.dtsi
index 06b3be2d5c0a..588973fbd840 100644
--- a/arch/arm/boot/dts/qcom/sdm660-vidc.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-vidc.dtsi
@@ -209,7 +209,7 @@
<&mmss_bimc_smmu 0x411>,
<&mmss_bimc_smmu 0x431>;
buffer-types = <0xfff>;
- virtual-addr-pool = <0x70800000 0x6f800000>;
+ virtual-addr-pool = <0x79000000 0x60000000>;
};
firmware_cb {
@@ -231,7 +231,7 @@
<&mmss_bimc_smmu 0x529>,
<&mmss_bimc_smmu 0x52b>;
buffer-types = <0x241>;
- virtual-addr-pool = <0x4b000000 0x25800000>;
+ virtual-addr-pool = <0x51000000 0x28000000>;
qcom,secure-context-bank;
};
@@ -243,7 +243,7 @@
<&mmss_bimc_smmu 0x510>,
<&mmss_bimc_smmu 0x52c>;
buffer-types = <0x106>;
- virtual-addr-pool = <0x25800000 0x25800000>;
+ virtual-addr-pool = <0x29000000 0x28000000>;
qcom,secure-context-bank;
};
@@ -260,7 +260,7 @@
<&mmss_bimc_smmu 0x52d>,
<&mmss_bimc_smmu 0x540>;
buffer-types = <0x480>;
- virtual-addr-pool = <0x1000000 0x24800000>;
+ virtual-addr-pool = <0x1000000 0x28000000>;
qcom,secure-context-bank;
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi
index 2e576a51677f..d13a359bae2e 100644
--- a/arch/arm/boot/dts/qcom/sdm660.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660.dtsi
@@ -2287,6 +2287,7 @@
lanes-per-direction = <1>;
spm-level = <5>;
+ non-removable;
qcom,msm-bus,name = "ufs1";
qcom,msm-bus,num-cases = <12>;
qcom,msm-bus,num-paths = <2>;
diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts
index 8ad7ca7f9743..7cf55acf900b 100644
--- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts
+++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts
@@ -261,88 +261,107 @@
qcom,msm-cpudai-afe-clk-ver = <2>;
};
- qcom,msm-dai-mi2s {
- compatible = "qcom,msm-dai-mi2s";
- dai_mi2s_sec: qcom,msm-dai-q6-mi2s-sec {
- compatible = "qcom,msm-dai-q6-mi2s";
- qcom,msm-dai-q6-mi2s-dev-id = <1>;
- qcom,msm-mi2s-rx-lines = <2>;
- qcom,msm-mi2s-tx-lines = <1>;
- };
-
- dai_mi2s: qcom,msm-dai-q6-mi2s-tert {
- compatible = "qcom,msm-dai-q6-mi2s";
- qcom,msm-dai-q6-mi2s-dev-id = <2>;
- qcom,msm-mi2s-rx-lines = <2>;
- qcom,msm-mi2s-tx-lines = <1>;
- };
-
- dai_mi2s_quat: qcom,msm-dai-q6-mi2s-quat {
- compatible = "qcom,msm-dai-q6-mi2s";
- qcom,msm-dai-q6-mi2s-dev-id = <3>;
- qcom,msm-mi2s-rx-lines = <1>;
- qcom,msm-mi2s-tx-lines = <0>;
- };
+ qcom,msm-dai-mi2s {
+ compatible = "qcom,msm-dai-mi2s";
+ dai_mi2s_sec: qcom,msm-dai-q6-mi2s-sec {
+ compatible = "qcom,msm-dai-q6-mi2s";
+ qcom,msm-dai-q6-mi2s-dev-id = <1>;
+ qcom,msm-mi2s-rx-lines = <2>;
+ qcom,msm-mi2s-tx-lines = <1>;
};
+ dai_mi2s: qcom,msm-dai-q6-mi2s-tert {
+ compatible = "qcom,msm-dai-q6-mi2s";
+ qcom,msm-dai-q6-mi2s-dev-id = <2>;
+ qcom,msm-mi2s-rx-lines = <2>;
+ qcom,msm-mi2s-tx-lines = <1>;
+ };
+
+ dai_mi2s_quat: qcom,msm-dai-q6-mi2s-quat {
+ compatible = "qcom,msm-dai-q6-mi2s";
+ qcom,msm-dai-q6-mi2s-dev-id = <3>;
+ qcom,msm-mi2s-rx-lines = <1>;
+ qcom,msm-mi2s-tx-lines = <0>;
+ };
+ };
+
+ qcom,msm-dai-tdm-sec-tx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37137>;
+ qcom,msm-cpudai-tdm-group-num-ports = <4>;
+ qcom,msm-cpudai-tdm-group-port-id = <36881 36883 36885 36887>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <0>;
+ 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>;
+ dai_sec_tdm_tx_0: qcom,msm-dai-q6-tdm-sec-tx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36881>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_sec_tdm_tx_1: qcom,msm-dai-q6-tdm-sec-tx-1 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36883>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_sec_tdm_tx_2: qcom,msm-dai-q6-tdm-sec-tx-2 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36885>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_sec_tdm_tx_3: qcom,msm-dai-q6-tdm-sec-tx-3 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36887>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
qcom,msm-dai-tdm-tert-rx {
compatible = "qcom,msm-dai-tdm";
qcom,msm-cpudai-tdm-group-id = <37152>;
qcom,msm-cpudai-tdm-group-num-ports = <5>;
- qcom,msm-cpudai-tdm-group-port-id =
- <36896 36898 36900 36902 36904>;
- qcom,msm-cpudai-tdm-clk-rate = <0>;
+ qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900
+ 36902 36904>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <0>;
+ 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>;
dai_tert_tdm_rx_0: qcom,msm-dai-q6-tdm-tert-rx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36896>;
- qcom,msm-cpudai-tdm-sync-mode = <1>;
- qcom,msm-cpudai-tdm-sync-src = <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>;
};
dai_tert_tdm_rx_1: qcom,msm-dai-q6-tdm-tert-rx-1 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36898>;
- 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>;
};
dai_tert_tdm_rx_2: qcom,msm-dai-q6-tdm-tert-rx-2 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36900>;
- 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>;
};
dai_tert_tdm_rx_3: qcom,msm-dai-q6-tdm-tert-rx-3 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36902>;
- 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>;
};
+
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>;
};
};
@@ -352,48 +371,34 @@
qcom,msm-cpudai-tdm-group-id = <37153>;
qcom,msm-cpudai-tdm-group-num-ports = <4>;
qcom,msm-cpudai-tdm-group-port-id = <36897 36899 36901 36903>;
- qcom,msm-cpudai-tdm-clk-rate = <0>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <0>;
+ 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>;
dai_tert_tdm_tx_0: qcom,msm-dai-q6-tdm-tert-tx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36897>;
- qcom,msm-cpudai-tdm-sync-mode = <1>;
- qcom,msm-cpudai-tdm-sync-src = <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>;
};
dai_tert_tdm_tx_1: qcom,msm-dai-q6-tdm-tert-tx-1 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36899>;
- 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>;
};
dai_tert_tdm_tx_2: qcom,msm-dai-q6-tdm-tert-tx-2 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36901>;
- 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>;
};
dai_tert_tdm_tx_3: qcom,msm-dai-q6-tdm-tert-tx-3 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36903>;
- 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>;
};
};
@@ -403,48 +408,34 @@
qcom,msm-cpudai-tdm-group-id = <37168>;
qcom,msm-cpudai-tdm-group-num-ports = <4>;
qcom,msm-cpudai-tdm-group-port-id = <36912 36914 36916 36918>;
- qcom,msm-cpudai-tdm-clk-rate = <0>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <0>;
+ 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>;
dai_quat_tdm_rx_0: qcom,msm-dai-q6-tdm-quat-rx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36912>;
- 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>;
};
dai_quat_tdm_rx_1: qcom,msm-dai-q6-tdm-quat-rx-1 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36914>;
- 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>;
};
dai_quat_tdm_rx_2: qcom,msm-dai-q6-tdm-quat-rx-2 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36916>;
- 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>;
};
dai_quat_tdm_rx_3: qcom,msm-dai-q6-tdm-quat-rx-3 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36918>;
- 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>;
};
};
@@ -454,96 +445,34 @@
qcom,msm-cpudai-tdm-group-id = <37169>;
qcom,msm-cpudai-tdm-group-num-ports = <4>;
qcom,msm-cpudai-tdm-group-port-id = <36913 36915 36917 36919>;
- qcom,msm-cpudai-tdm-clk-rate = <0>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <0>;
+ 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>;
dai_quat_tdm_tx_0: qcom,msm-dai-q6-tdm-quat-tx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36913>;
- 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>;
};
dai_quat_tdm_tx_1: qcom,msm-dai-q6-tdm-quat-tx-1 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36915>;
- 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>;
};
dai_quat_tdm_tx_2: qcom,msm-dai-q6-tdm-quat-tx-2 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36917>;
- 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>;
};
dai_quat_tdm_tx_3: qcom,msm-dai-q6-tdm-quat-tx-3 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36919>;
- 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-sec-tx {
- compatible = "qcom,msm-dai-tdm";
- qcom,msm-cpudai-tdm-group-id = <37137>;
- qcom,msm-cpudai-tdm-group-num-ports = <4>;
- qcom,msm-cpudai-tdm-group-port-id = <36881 36883 36885 36887>;
- qcom,msm-cpudai-tdm-clk-rate = <0>;
- dai_sec_tdm_tx_0: qcom,msm-dai-q6-tdm-sec-tx-0 {
- compatible = "qcom,msm-dai-q6-tdm";
- qcom,msm-cpudai-tdm-dev-id = <36881>;
- 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>;
- };
- dai_sec_tdm_tx_1: qcom,msm-dai-q6-tdm-sec-tx-1 {
- compatible = "qcom,msm-dai-q6-tdm";
- qcom,msm-cpudai-tdm-dev-id = <36883>;
- 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>;
- };
- dai_sec_tdm_tx_2: qcom,msm-dai-q6-tdm-sec-tx-2 {
- compatible = "qcom,msm-dai-q6-tdm";
- qcom,msm-cpudai-tdm-dev-id = <36885>;
- 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>;
- };
- dai_sec_tdm_tx_3: qcom,msm-dai-q6-tdm-sec-tx-3 {
- compatible = "qcom,msm-dai-q6-tdm";
- qcom,msm-cpudai-tdm-dev-id = <36887>;
- 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>;
};
};
diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts
index ed7e1009326c..d9ee0fd817e9 100644
--- a/arch/arm/boot/dts/tegra20-paz00.dts
+++ b/arch/arm/boot/dts/tegra20-paz00.dts
@@ -565,6 +565,7 @@
regulator-name = "+3VS,vdd_pnl";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
gpio = <&gpio TEGRA_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
enable-active-high;
};
diff --git a/arch/arm/kvm/psci.c b/arch/arm/kvm/psci.c
index a9b3b905e661..443db0c43d7c 100644
--- a/arch/arm/kvm/psci.c
+++ b/arch/arm/kvm/psci.c
@@ -208,9 +208,10 @@ int kvm_psci_version(struct kvm_vcpu *vcpu)
static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
{
- int ret = 1;
+ struct kvm *kvm = vcpu->kvm;
unsigned long psci_fn = vcpu_get_reg(vcpu, 0) & ~((u32) 0);
unsigned long val;
+ int ret = 1;
switch (psci_fn) {
case PSCI_0_2_FN_PSCI_VERSION:
@@ -230,7 +231,9 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
break;
case PSCI_0_2_FN_CPU_ON:
case PSCI_0_2_FN64_CPU_ON:
+ mutex_lock(&kvm->lock);
val = kvm_psci_vcpu_on(vcpu);
+ mutex_unlock(&kvm->lock);
break;
case PSCI_0_2_FN_AFFINITY_INFO:
case PSCI_0_2_FN64_AFFINITY_INFO:
@@ -279,6 +282,7 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
{
+ struct kvm *kvm = vcpu->kvm;
unsigned long psci_fn = vcpu_get_reg(vcpu, 0) & ~((u32) 0);
unsigned long val;
@@ -288,7 +292,9 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
val = PSCI_RET_SUCCESS;
break;
case KVM_PSCI_FN_CPU_ON:
+ mutex_lock(&kvm->lock);
val = kvm_psci_vcpu_on(vcpu);
+ mutex_unlock(&kvm->lock);
break;
default:
val = PSCI_RET_NOT_SUPPORTED;
diff --git a/arch/arm64/configs/msm-auto-perf_defconfig b/arch/arm64/configs/msm-auto-perf_defconfig
index c9dfe25b938a..d2a0fc53235d 100644
--- a/arch/arm64/configs/msm-auto-perf_defconfig
+++ b/arch/arm64/configs/msm-auto-perf_defconfig
@@ -225,6 +225,7 @@ CONFIG_MSM_BT_POWER=y
CONFIG_BTFM_SLIM=y
CONFIG_BTFM_SLIM_WCN3990=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
CONFIG_IPC_ROUTER=y
CONFIG_IPC_ROUTER_SECURITY=y
diff --git a/arch/arm64/configs/msm-auto_defconfig b/arch/arm64/configs/msm-auto_defconfig
index 7a139efa1455..e67df6338136 100644
--- a/arch/arm64/configs/msm-auto_defconfig
+++ b/arch/arm64/configs/msm-auto_defconfig
@@ -227,6 +227,7 @@ CONFIG_MSM_BT_POWER=y
CONFIG_BTFM_SLIM=y
CONFIG_BTFM_SLIM_WCN3990=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
CONFIG_IPC_ROUTER=y
CONFIG_IPC_ROUTER_SECURITY=y
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 6f5be663140f..5adb7697cc3e 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -32,7 +32,6 @@ CONFIG_BLK_DEV_INITRD=y
# CONFIG_RD_XZ is not set
# CONFIG_RD_LZO is not set
# CONFIG_RD_LZ4 is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_KALLSYMS_ALL=y
# CONFIG_AIO is not set
# CONFIG_MEMBARRIER is not set
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index f09a134a2fd5..b362a0561d33 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -32,7 +32,6 @@ CONFIG_BLK_DEV_INITRD=y
# CONFIG_RD_XZ is not set
# CONFIG_RD_LZO is not set
# CONFIG_RD_LZ4 is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_KALLSYMS_ALL=y
# CONFIG_AIO is not set
# CONFIG_MEMBARRIER is not set
@@ -486,6 +485,7 @@ CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_USB_CONFIGFS_F_CCID=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_RING_BUFFER=y
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig
index 939b34f7d6dd..6d6fd23095d5 100644
--- a/arch/arm64/configs/sdm660-perf_defconfig
+++ b/arch/arm64/configs/sdm660-perf_defconfig
@@ -7,6 +7,8 @@ CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_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
diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig
index aafde733099b..25566e45c46f 100644
--- a/arch/arm64/configs/sdm660_defconfig
+++ b/arch/arm64/configs/sdm660_defconfig
@@ -8,6 +8,9 @@ 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
diff --git a/arch/arm64/include/asm/cmpxchg.h b/arch/arm64/include/asm/cmpxchg.h
index 510c7b404454..270c6b7b0a61 100644
--- a/arch/arm64/include/asm/cmpxchg.h
+++ b/arch/arm64/include/asm/cmpxchg.h
@@ -48,7 +48,7 @@ static inline unsigned long __xchg_case_##name(unsigned long x, \
" swp" #acq_lse #rel #sz "\t%" #w "3, %" #w "0, %2\n" \
" nop\n" \
" " #nop_lse) \
- : "=&r" (ret), "=&r" (tmp), "+Q" (*(u8 *)ptr) \
+ : "=&r" (ret), "=&r" (tmp), "+Q" (*(unsigned long *)ptr) \
: "r" (x) \
: cl); \
\
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index efafdf39cb3b..ac177d96e773 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -108,11 +108,12 @@ static inline void set_fs(mm_segment_t fs)
*/
#define __range_ok(addr, size) \
({ \
+ unsigned long __addr = (unsigned long __force)(addr); \
unsigned long flag, roksum; \
__chk_user_ptr(addr); \
asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, ls" \
: "=&r" (flag), "=&r" (roksum) \
- : "1" (addr), "Ir" (size), \
+ : "1" (__addr), "Ir" (size), \
"r" (current_thread_info()->addr_limit) \
: "cc"); \
flag; \
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index eec3598b4184..3ff507c177a5 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1055,8 +1055,8 @@ static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
{
struct sys_reg_params params;
u32 hsr = kvm_vcpu_get_hsr(vcpu);
- int Rt = (hsr >> 5) & 0xf;
- int Rt2 = (hsr >> 10) & 0xf;
+ int Rt = (hsr >> 5) & 0x1f;
+ int Rt2 = (hsr >> 10) & 0x1f;
params.is_aarch32 = true;
params.is_32bit = false;
@@ -1107,7 +1107,7 @@ static int kvm_handle_cp_32(struct kvm_vcpu *vcpu,
{
struct sys_reg_params params;
u32 hsr = kvm_vcpu_get_hsr(vcpu);
- int Rt = (hsr >> 5) & 0xf;
+ int Rt = (hsr >> 5) & 0x1f;
params.is_aarch32 = true;
params.is_32bit = true;
diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h
index 07238b39638c..3db381205928 100644
--- a/arch/metag/include/asm/uaccess.h
+++ b/arch/metag/include/asm/uaccess.h
@@ -28,24 +28,32 @@
#define segment_eq(a, b) ((a).seg == (b).seg)
-#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
-/*
- * Explicitly allow NULL pointers here. Parts of the kernel such
- * as readv/writev use access_ok to validate pointers, but want
- * to allow NULL pointers for various reasons. NULL pointers are
- * safe to allow through because the first page is not mappable on
- * Meta.
- *
- * We also wish to avoid letting user code access the system area
- * and the kernel half of the address space.
- */
-#define __user_bad(addr, size) (((addr) > 0 && (addr) < META_MEMORY_BASE) || \
- ((addr) > PAGE_OFFSET && \
- (addr) < LINCORE_BASE))
-
static inline int __access_ok(unsigned long addr, unsigned long size)
{
- return __kernel_ok || !__user_bad(addr, size);
+ /*
+ * Allow access to the user mapped memory area, but not the system area
+ * before it. The check extends to the top of the address space when
+ * kernel access is allowed (there's no real reason to user copy to the
+ * system area in any case).
+ */
+ if (likely(addr >= META_MEMORY_BASE && addr < get_fs().seg &&
+ size <= get_fs().seg - addr))
+ return true;
+ /*
+ * Explicitly allow NULL pointers here. Parts of the kernel such
+ * as readv/writev use access_ok to validate pointers, but want
+ * to allow NULL pointers for various reasons. NULL pointers are
+ * safe to allow through because the first page is not mappable on
+ * Meta.
+ */
+ if (!addr)
+ return true;
+ /* Allow access to core code memory area... */
+ if (addr >= LINCORE_CODE_BASE && addr <= LINCORE_CODE_LIMIT &&
+ size <= LINCORE_CODE_LIMIT + 1 - addr)
+ return true;
+ /* ... but no other areas. */
+ return false;
}
#define access_ok(type, addr, size) __access_ok((unsigned long)(addr), \
@@ -186,8 +194,13 @@ do { \
extern long __must_check __strncpy_from_user(char *dst, const char __user *src,
long count);
-#define strncpy_from_user(dst, src, count) __strncpy_from_user(dst, src, count)
-
+static inline long
+strncpy_from_user(char *dst, const char __user *src, long count)
+{
+ if (!access_ok(VERIFY_READ, src, 1))
+ return -EFAULT;
+ return __strncpy_from_user(dst, src, count);
+}
/*
* Return the size of a string (including the ending 0)
*
diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S
index 488e6314f993..5cc93f0b52ca 100644
--- a/arch/powerpc/kernel/exceptions-64e.S
+++ b/arch/powerpc/kernel/exceptions-64e.S
@@ -735,8 +735,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
andis. r15,r14,(DBSR_IC|DBSR_BT)@h
beq+ 1f
+#ifdef CONFIG_RELOCATABLE
+ ld r15,PACATOC(r13)
+ ld r14,interrupt_base_book3e@got(r15)
+ ld r15,__end_interrupts@got(r15)
+#else
LOAD_REG_IMMEDIATE(r14,interrupt_base_book3e)
LOAD_REG_IMMEDIATE(r15,__end_interrupts)
+#endif
cmpld cr0,r10,r14
cmpld cr1,r10,r15
blt+ cr0,1f
@@ -799,8 +805,14 @@ kernel_dbg_exc:
andis. r15,r14,(DBSR_IC|DBSR_BT)@h
beq+ 1f
+#ifdef CONFIG_RELOCATABLE
+ ld r15,PACATOC(r13)
+ ld r14,interrupt_base_book3e@got(r15)
+ ld r15,__end_interrupts@got(r15)
+#else
LOAD_REG_IMMEDIATE(r14,interrupt_base_book3e)
LOAD_REG_IMMEDIATE(r15,__end_interrupts)
+#endif
cmpld cr0,r10,r14
cmpld cr1,r10,r15
blt+ cr0,1f
diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c
index b2eb4686bd8f..da3c4c3f4ec8 100644
--- a/arch/powerpc/kernel/mce.c
+++ b/arch/powerpc/kernel/mce.c
@@ -204,6 +204,8 @@ static void machine_check_process_queued_event(struct irq_work *work)
{
int index;
+ add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
+
/*
* For now just print it to console.
* TODO: log this error event to FSP or nvram.
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 37de90f8a845..e4dcb0a43e3f 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -297,8 +297,6 @@ long machine_check_early(struct pt_regs *regs)
__this_cpu_inc(irq_stat.mce_exceptions);
- add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
-
if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
handled = cur_cpu_spec->machine_check_early(regs);
return handled;
@@ -704,6 +702,8 @@ void machine_check_exception(struct pt_regs *regs)
__this_cpu_inc(irq_stat.mce_exceptions);
+ add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
+
/* See if any machine dependent calls. In theory, we would want
* to call the CPU first, and call the ppc_md. one if the CPU
* one returns a positive number. However there is existing code
diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c
index f244dcb4f2cf..96536c969c9c 100644
--- a/arch/powerpc/platforms/pseries/dlpar.c
+++ b/arch/powerpc/platforms/pseries/dlpar.c
@@ -280,7 +280,6 @@ int dlpar_detach_node(struct device_node *dn)
if (rc)
return rc;
- of_node_put(dn); /* Must decrement the refcount */
return 0;
}
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index f7c3a61040bd..df4685905015 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -464,6 +464,20 @@ static void *nt_vmcoreinfo(void *ptr)
}
/*
+ * Initialize final note (needed for /proc/vmcore code)
+ */
+static void *nt_final(void *ptr)
+{
+ Elf64_Nhdr *note;
+
+ note = (Elf64_Nhdr *) ptr;
+ note->n_namesz = 0;
+ note->n_descsz = 0;
+ note->n_type = 0;
+ return PTR_ADD(ptr, sizeof(Elf64_Nhdr));
+}
+
+/*
* Initialize ELF header (new kernel)
*/
static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt)
@@ -553,6 +567,7 @@ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
ptr = fill_cpu_elf_notes(ptr, &sa_ext->sa, sa_ext->vx_regs);
}
ptr = nt_vmcoreinfo(ptr);
+ ptr = nt_final(ptr);
memset(phdr, 0, sizeof(*phdr));
phdr->p_type = PT_NOTE;
phdr->p_offset = notes_offset;
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 424e6809ad07..7460df3eec6b 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -308,6 +308,7 @@ ENTRY(system_call)
lg %r14,__LC_VDSO_PER_CPU
lmg %r0,%r10,__PT_R0(%r11)
mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
+.Lsysc_exit_timer:
stpt __LC_EXIT_TIMER
mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
lmg %r11,%r15,__PT_R11(%r11)
@@ -593,6 +594,7 @@ ENTRY(io_int_handler)
lg %r14,__LC_VDSO_PER_CPU
lmg %r0,%r10,__PT_R0(%r11)
mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
+.Lio_exit_timer:
stpt __LC_EXIT_TIMER
mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
lmg %r11,%r15,__PT_R11(%r11)
@@ -1118,15 +1120,23 @@ cleanup_critical:
br %r14
.Lcleanup_sysc_restore:
+ # check if stpt has been executed
clg %r9,BASED(.Lcleanup_sysc_restore_insn)
+ jh 0f
+ mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER
+ cghi %r11,__LC_SAVE_AREA_ASYNC
je 0f
+ mvc __LC_EXIT_TIMER(8),__LC_MCCK_ENTER_TIMER
+0: clg %r9,BASED(.Lcleanup_sysc_restore_insn+8)
+ je 1f
lg %r9,24(%r11) # get saved pointer to pt_regs
mvc __LC_RETURN_PSW(16),__PT_PSW(%r9)
mvc 0(64,%r11),__PT_R8(%r9)
lmg %r0,%r7,__PT_R0(%r9)
-0: lmg %r8,%r9,__LC_RETURN_PSW
+1: lmg %r8,%r9,__LC_RETURN_PSW
br %r14
.Lcleanup_sysc_restore_insn:
+ .quad .Lsysc_exit_timer
.quad .Lsysc_done - 4
.Lcleanup_io_tif:
@@ -1134,15 +1144,20 @@ cleanup_critical:
br %r14
.Lcleanup_io_restore:
+ # check if stpt has been executed
clg %r9,BASED(.Lcleanup_io_restore_insn)
- je 0f
+ jh 0f
+ mvc __LC_EXIT_TIMER(8),__LC_MCCK_ENTER_TIMER
+0: clg %r9,BASED(.Lcleanup_io_restore_insn+8)
+ je 1f
lg %r9,24(%r11) # get saved r11 pointer to pt_regs
mvc __LC_RETURN_PSW(16),__PT_PSW(%r9)
mvc 0(64,%r11),__PT_R8(%r9)
lmg %r0,%r7,__PT_R0(%r9)
-0: lmg %r8,%r9,__LC_RETURN_PSW
+1: lmg %r8,%r9,__LC_RETURN_PSW
br %r14
.Lcleanup_io_restore_insn:
+ .quad .Lio_exit_timer
.quad .Lio_done - 4
.Lcleanup_idle:
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
index 9011a88353de..ed1e9206f830 100644
--- a/arch/x86/boot/boot.h
+++ b/arch/x86/boot/boot.h
@@ -16,7 +16,7 @@
#ifndef BOOT_BOOT_H
#define BOOT_BOOT_H
-#define STACK_SIZE 512 /* Minimum number of bytes for stack */
+#define STACK_SIZE 1024 /* Minimum number of bytes for stack */
#ifndef __ASSEMBLY__
diff --git a/arch/x86/include/asm/pmem.h b/arch/x86/include/asm/pmem.h
index bd8ce6bcdfc9..6503526d7b24 100644
--- a/arch/x86/include/asm/pmem.h
+++ b/arch/x86/include/asm/pmem.h
@@ -122,7 +122,7 @@ static inline size_t arch_copy_from_iter_pmem(void __pmem *addr, size_t bytes,
if (bytes < 8) {
if (!IS_ALIGNED(dest, 4) || (bytes != 4))
- __arch_wb_cache_pmem(addr, 1);
+ __arch_wb_cache_pmem(addr, bytes);
} else {
if (!IS_ALIGNED(dest, 8)) {
dest = ALIGN(dest, boot_cpu_data.x86_clflush_size);
diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c
index be39b5fde4b9..1011c05b1bd5 100644
--- a/arch/x86/kernel/fpu/init.c
+++ b/arch/x86/kernel/fpu/init.c
@@ -96,6 +96,7 @@ static void fpu__init_system_early_generic(struct cpuinfo_x86 *c)
* Boot time FPU feature detection code:
*/
unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
+EXPORT_SYMBOL_GPL(mxcsr_feature_mask);
static void __init fpu__init_system_mxcsr(void)
{
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e75095fa414e..ae2b9cd358f2 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2960,6 +2960,12 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
| KVM_VCPUEVENT_VALID_SMM))
return -EINVAL;
+ /* INITs are latched while in SMM */
+ if (events->flags & KVM_VCPUEVENT_VALID_SMM &&
+ (events->smi.smm || events->smi.pending) &&
+ vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED)
+ return -EINVAL;
+
process_nmi(vcpu);
vcpu->arch.exception.pending = events->exception.injected;
vcpu->arch.exception.nr = events->exception.nr;
@@ -3134,11 +3140,14 @@ static void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu,
}
}
+#define XSAVE_MXCSR_OFFSET 24
+
static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
struct kvm_xsave *guest_xsave)
{
u64 xstate_bv =
*(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)];
+ u32 mxcsr = *(u32 *)&guest_xsave->region[XSAVE_MXCSR_OFFSET / sizeof(u32)];
if (cpu_has_xsave) {
/*
@@ -3146,11 +3155,13 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
* CPUID leaf 0xD, index 0, EDX:EAX. This is for compatibility
* with old userspace.
*/
- if (xstate_bv & ~kvm_supported_xcr0())
+ if (xstate_bv & ~kvm_supported_xcr0() ||
+ mxcsr & ~mxcsr_feature_mask)
return -EINVAL;
load_xsave(vcpu, (u8 *)guest_xsave->region);
} else {
- if (xstate_bv & ~XFEATURE_MASK_FPSSE)
+ if (xstate_bv & ~XFEATURE_MASK_FPSSE ||
+ mxcsr & ~mxcsr_feature_mask)
return -EINVAL;
memcpy(&vcpu->arch.guest_fpu.state.fxsave,
guest_xsave->region, sizeof(struct fxregs_state));
@@ -4597,16 +4608,20 @@ emul_write:
static int kernel_pio(struct kvm_vcpu *vcpu, void *pd)
{
- /* TODO: String I/O for in kernel device */
- int r;
+ int r = 0, i;
- if (vcpu->arch.pio.in)
- r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, vcpu->arch.pio.port,
- vcpu->arch.pio.size, pd);
- else
- r = kvm_io_bus_write(vcpu, KVM_PIO_BUS,
- vcpu->arch.pio.port, vcpu->arch.pio.size,
- pd);
+ for (i = 0; i < vcpu->arch.pio.count; i++) {
+ if (vcpu->arch.pio.in)
+ r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, vcpu->arch.pio.port,
+ vcpu->arch.pio.size, pd);
+ else
+ r = kvm_io_bus_write(vcpu, KVM_PIO_BUS,
+ vcpu->arch.pio.port, vcpu->arch.pio.size,
+ pd);
+ if (r)
+ break;
+ pd += vcpu->arch.pio.size;
+ }
return r;
}
@@ -4644,6 +4659,8 @@ static int emulator_pio_in_emulated(struct x86_emulate_ctxt *ctxt,
if (vcpu->arch.pio.count)
goto data_avail;
+ memset(vcpu->arch.pio_data, 0, size * count);
+
ret = emulator_pio_in_out(vcpu, size, port, val, count, true);
if (ret) {
data_avail:
@@ -6993,6 +7010,12 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
mp_state->mp_state != KVM_MP_STATE_RUNNABLE)
return -EINVAL;
+ /* INITs are latched while in SMM */
+ if ((is_smm(vcpu) || vcpu->arch.smi_pending) &&
+ (mp_state->mp_state == KVM_MP_STATE_SIPI_RECEIVED ||
+ mp_state->mp_state == KVM_MP_STATE_INIT_RECEIVED))
+ return -EINVAL;
+
if (mp_state->mp_state == KVM_MP_STATE_SIPI_RECEIVED) {
vcpu->arch.mp_state = KVM_MP_STATE_INIT_RECEIVED;
set_bit(KVM_APIC_SIPI, &vcpu->arch.apic->pending_events);
diff --git a/arch/x86/um/ptrace_64.c b/arch/x86/um/ptrace_64.c
index a629694ee750..e14c43a2d187 100644
--- a/arch/x86/um/ptrace_64.c
+++ b/arch/x86/um/ptrace_64.c
@@ -121,7 +121,7 @@ int poke_user(struct task_struct *child, long addr, long data)
else if ((addr >= offsetof(struct user, u_debugreg[0])) &&
(addr <= offsetof(struct user, u_debugreg[7]))) {
addr -= offsetof(struct user, u_debugreg[0]);
- addr = addr >> 2;
+ addr = addr >> 3;
if ((addr == 4) || (addr == 5))
return -EIO;
child->thread.arch.debugregs[addr] = data;
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index 1e56ff583459..63146c378f1e 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -2038,7 +2038,8 @@ static unsigned long __init xen_read_phys_ulong(phys_addr_t addr)
/*
* Translate a virtual address to a physical one without relying on mapped
- * page tables.
+ * page tables. Don't rely on big pages being aligned in (guest) physical
+ * space!
*/
static phys_addr_t __init xen_early_virt_to_phys(unsigned long vaddr)
{
@@ -2059,7 +2060,7 @@ static phys_addr_t __init xen_early_virt_to_phys(unsigned long vaddr)
sizeof(pud)));
if (!pud_present(pud))
return 0;
- pa = pud_pfn(pud) << PAGE_SHIFT;
+ pa = pud_val(pud) & PTE_PFN_MASK;
if (pud_large(pud))
return pa + (vaddr & ~PUD_MASK);
@@ -2067,7 +2068,7 @@ static phys_addr_t __init xen_early_virt_to_phys(unsigned long vaddr)
sizeof(pmd)));
if (!pmd_present(pmd))
return 0;
- pa = pmd_pfn(pmd) << PAGE_SHIFT;
+ pa = pmd_val(pmd) & PTE_PFN_MASK;
if (pmd_large(pmd))
return pa + (vaddr & ~PMD_MASK);
diff --git a/block/blk-integrity.c b/block/blk-integrity.c
index 319f2e4f4a8b..478f572cb1e7 100644
--- a/block/blk-integrity.c
+++ b/block/blk-integrity.c
@@ -412,7 +412,8 @@ void blk_integrity_register(struct gendisk *disk, struct blk_integrity *template
bi->flags = BLK_INTEGRITY_VERIFY | BLK_INTEGRITY_GENERATE |
template->flags;
- bi->interval_exp = ilog2(queue_logical_block_size(disk->queue));
+ bi->interval_exp = template->interval_exp ? :
+ ilog2(queue_logical_block_size(disk->queue));
bi->profile = template->profile ? template->profile : &nop_profile;
bi->tuple_size = template->tuple_size;
bi->tag_size = template->tag_size;
diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c
index 6d4d4569447e..faea9d728fd2 100644
--- a/crypto/algif_aead.c
+++ b/crypto/algif_aead.c
@@ -29,6 +29,11 @@ struct aead_sg_list {
struct scatterlist sg[ALG_MAX_PAGES];
};
+struct aead_tfm {
+ struct crypto_aead *aead;
+ bool has_key;
+};
+
struct aead_ctx {
struct aead_sg_list tsgl;
/*
@@ -513,24 +518,146 @@ static struct proto_ops algif_aead_ops = {
.poll = aead_poll,
};
+static int aead_check_key(struct socket *sock)
+{
+ int err = 0;
+ struct sock *psk;
+ struct alg_sock *pask;
+ struct aead_tfm *tfm;
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+
+ lock_sock(sk);
+ if (ask->refcnt)
+ goto unlock_child;
+
+ psk = ask->parent;
+ pask = alg_sk(ask->parent);
+ tfm = pask->private;
+
+ err = -ENOKEY;
+ lock_sock_nested(psk, SINGLE_DEPTH_NESTING);
+ if (!tfm->has_key)
+ goto unlock;
+
+ if (!pask->refcnt++)
+ sock_hold(psk);
+
+ ask->refcnt = 1;
+ sock_put(psk);
+
+ err = 0;
+
+unlock:
+ release_sock(psk);
+unlock_child:
+ release_sock(sk);
+
+ return err;
+}
+
+static int aead_sendmsg_nokey(struct socket *sock, struct msghdr *msg,
+ size_t size)
+{
+ int err;
+
+ err = aead_check_key(sock);
+ if (err)
+ return err;
+
+ return aead_sendmsg(sock, msg, size);
+}
+
+static ssize_t aead_sendpage_nokey(struct socket *sock, struct page *page,
+ int offset, size_t size, int flags)
+{
+ int err;
+
+ err = aead_check_key(sock);
+ if (err)
+ return err;
+
+ return aead_sendpage(sock, page, offset, size, flags);
+}
+
+static int aead_recvmsg_nokey(struct socket *sock, struct msghdr *msg,
+ size_t ignored, int flags)
+{
+ int err;
+
+ err = aead_check_key(sock);
+ if (err)
+ return err;
+
+ return aead_recvmsg(sock, msg, ignored, flags);
+}
+
+static struct proto_ops algif_aead_ops_nokey = {
+ .family = PF_ALG,
+
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .getname = sock_no_getname,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .accept = sock_no_accept,
+ .setsockopt = sock_no_setsockopt,
+
+ .release = af_alg_release,
+ .sendmsg = aead_sendmsg_nokey,
+ .sendpage = aead_sendpage_nokey,
+ .recvmsg = aead_recvmsg_nokey,
+ .poll = aead_poll,
+};
+
static void *aead_bind(const char *name, u32 type, u32 mask)
{
- return crypto_alloc_aead(name, type, mask);
+ struct aead_tfm *tfm;
+ struct crypto_aead *aead;
+
+ tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
+ if (!tfm)
+ return ERR_PTR(-ENOMEM);
+
+ aead = crypto_alloc_aead(name, type, mask);
+ if (IS_ERR(aead)) {
+ kfree(tfm);
+ return ERR_CAST(aead);
+ }
+
+ tfm->aead = aead;
+
+ return tfm;
}
static void aead_release(void *private)
{
- crypto_free_aead(private);
+ struct aead_tfm *tfm = private;
+
+ crypto_free_aead(tfm->aead);
+ kfree(tfm);
}
static int aead_setauthsize(void *private, unsigned int authsize)
{
- return crypto_aead_setauthsize(private, authsize);
+ struct aead_tfm *tfm = private;
+
+ return crypto_aead_setauthsize(tfm->aead, authsize);
}
static int aead_setkey(void *private, const u8 *key, unsigned int keylen)
{
- return crypto_aead_setkey(private, key, keylen);
+ struct aead_tfm *tfm = private;
+ int err;
+
+ err = crypto_aead_setkey(tfm->aead, key, keylen);
+ tfm->has_key = !err;
+
+ return err;
}
static void aead_sock_destruct(struct sock *sk)
@@ -546,12 +673,14 @@ static void aead_sock_destruct(struct sock *sk)
af_alg_release_parent(sk);
}
-static int aead_accept_parent(void *private, struct sock *sk)
+static int aead_accept_parent_nokey(void *private, struct sock *sk)
{
struct aead_ctx *ctx;
struct alg_sock *ask = alg_sk(sk);
- unsigned int len = sizeof(*ctx) + crypto_aead_reqsize(private);
- unsigned int ivlen = crypto_aead_ivsize(private);
+ struct aead_tfm *tfm = private;
+ struct crypto_aead *aead = tfm->aead;
+ unsigned int len = sizeof(*ctx) + crypto_aead_reqsize(aead);
+ unsigned int ivlen = crypto_aead_ivsize(aead);
ctx = sock_kmalloc(sk, len, GFP_KERNEL);
if (!ctx)
@@ -577,7 +706,7 @@ static int aead_accept_parent(void *private, struct sock *sk)
ask->private = ctx;
- aead_request_set_tfm(&ctx->aead_req, private);
+ aead_request_set_tfm(&ctx->aead_req, aead);
aead_request_set_callback(&ctx->aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
af_alg_complete, &ctx->completion);
@@ -586,13 +715,25 @@ static int aead_accept_parent(void *private, struct sock *sk)
return 0;
}
+static int aead_accept_parent(void *private, struct sock *sk)
+{
+ struct aead_tfm *tfm = private;
+
+ if (!tfm->has_key)
+ return -ENOKEY;
+
+ return aead_accept_parent_nokey(private, sk);
+}
+
static const struct af_alg_type algif_type_aead = {
.bind = aead_bind,
.release = aead_release,
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
.accept = aead_accept_parent,
+ .accept_nokey = aead_accept_parent_nokey,
.ops = &algif_aead_ops,
+ .ops_nokey = &algif_aead_ops_nokey,
.name = "aead",
.owner = THIS_MODULE
};
diff --git a/drivers/Makefile b/drivers/Makefile
index eb67aadf2ee0..2545cf95e8db 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_USB_PHY) += usb/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_PCI) += usb/
obj-$(CONFIG_USB_GADGET) += usb/
+obj-$(CONFIG_OF) += usb/
obj-$(CONFIG_SERIO) += input/serio/
obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_INPUT) += input/
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 212ca2eee257..68561696f31b 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -295,6 +295,7 @@ static void fw_free_buf(struct firmware_buf *buf)
{
struct firmware_cache *fwc = buf->fwc;
if (!fwc) {
+ kfree_const(buf->fw_id);
kfree(buf);
return;
}
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index cb852cc750b7..f9b569ef3dd7 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -287,6 +287,9 @@ static int bcm_open(struct hci_uart *hu)
hu->priv = bcm;
+ if (!hu->tty->dev)
+ goto out;
+
mutex_lock(&bcm_device_lock);
list_for_each(p, &bcm_device_list) {
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
@@ -307,7 +310,7 @@ static int bcm_open(struct hci_uart *hu)
}
mutex_unlock(&bcm_device_lock);
-
+out:
return 0;
}
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index b9065506a847..0c63fce0c1e0 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -307,6 +307,9 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
struct list_head *p;
int err = -ENODEV;
+ if (!hu->tty->dev)
+ return err;
+
mutex_lock(&intel_device_list_lock);
list_for_each(p, &intel_device_list) {
@@ -379,6 +382,9 @@ static void intel_busy_work(struct work_struct *work)
struct intel_data *intel = container_of(work, struct intel_data,
busy_work);
+ if (!intel->hu->tty->dev)
+ return;
+
/* Link is busy, delay the suspend */
mutex_lock(&intel_device_list_lock);
list_for_each(p, &intel_device_list) {
@@ -913,6 +919,8 @@ done:
list_for_each(p, &intel_device_list) {
struct intel_device *dev = list_entry(p, struct intel_device,
list);
+ if (!hu->tty->dev)
+ break;
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
if (device_may_wakeup(&dev->pdev->dev))
idev = dev;
@@ -1094,6 +1102,9 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
BT_DBG("hu %p skb %p", hu, skb);
+ if (!hu->tty->dev)
+ goto out_enqueue;
+
/* Be sure our controller is resumed and potential LPM transaction
* completed before enqueuing any packet.
*/
@@ -1110,7 +1121,7 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
}
}
mutex_unlock(&intel_device_list_lock);
-
+out_enqueue:
skb_queue_tail(&intel->txq, skb);
return 0;
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 14c833691194..ed0226131b90 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -670,7 +670,8 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, unsigned attr,
init_dma_attrs(&attrs);
dma_set_attr(DMA_ATTR_EXEC_MAPPING, &attrs);
- if (map->attr & FASTRPC_ATTR_NON_COHERENT)
+ if ((map->attr & FASTRPC_ATTR_NON_COHERENT) ||
+ (sess->smmu.coherent && map->uncached))
dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT,
&attrs);
else if (map->attr & FASTRPC_ATTR_COHERENT)
@@ -1686,6 +1687,9 @@ static int fastrpc_init_process(struct fastrpc_file *fl,
int namelen;
int pageslen;
} inbuf;
+
+ if (!init->filelen)
+ goto bail;
VERIFY(err, proc_name = kzalloc(init->filelen, GFP_KERNEL));
if (err)
goto bail;
@@ -1694,7 +1698,7 @@ static int fastrpc_init_process(struct fastrpc_file *fl,
if (err)
goto bail;
inbuf.pgid = current->tgid;
- inbuf.namelen = strlen(proc_name)+1;
+ inbuf.namelen = init->filelen;
inbuf.pageslen = 0;
if (!me->staticpd_flags) {
inbuf.pageslen = 1;
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index fb45af9c49d3..196e87b61705 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -2910,6 +2910,8 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry)
new_entry->num_buffers = 1;
break;
}
+
+ new_entry->buffers = NULL;
new_entry->real_time = MODE_REALTIME;
new_entry->in_service = 0;
INIT_LIST_HEAD(&new_entry->list_write_buf);
@@ -2983,7 +2985,8 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry)
fail_alloc:
if (new_entry) {
- for (i = 0; i < new_entry->num_buffers; i++) {
+ for (i = 0; ((i < new_entry->num_buffers) &&
+ new_entry->buffers); i++) {
proc_buf = &new_entry->buffers[i];
if (proc_buf) {
mutex_destroy(&proc_buf->health_mutex);
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index 382717bad828..21994d53db91 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -28,7 +28,8 @@
#define DIAG_SET_FEATURE_MASK(x) (feature_bytes[(x)/8] |= (1 << (x & 0x7)))
#define diag_check_update(x) \
- (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x)))) \
+ (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x))) \
+ || (info && (info->peripheral_mask & MD_PERIPHERAL_PD_MASK(x)))) \
struct diag_mask_info msg_mask;
struct diag_mask_info msg_bt_mask;
@@ -89,7 +90,7 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id)
int err = 0;
int send_once = 0;
int header_len = sizeof(struct diag_ctrl_log_mask);
- uint8_t *buf = NULL;
+ uint8_t *buf = NULL, upd = 0;
uint8_t *temp = NULL;
uint32_t mask_size = 0;
struct diag_ctrl_log_mask ctrl_pkt;
@@ -106,11 +107,25 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id)
return;
}
- if (driver->md_session_mask != 0 &&
- driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral))
- mask_info = driver->md_session_map[peripheral]->log_mask;
- else
+ if (driver->md_session_mask != 0) {
+ if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) {
+ if (driver->md_session_map[peripheral])
+ mask_info =
+ driver->md_session_map[peripheral]->log_mask;
+ } else if (driver->md_session_mask &
+ MD_PERIPHERAL_PD_MASK(peripheral)) {
+ upd = diag_mask_to_pd_value(driver->md_session_mask);
+ if (upd && driver->md_session_map[upd])
+ mask_info =
+ driver->md_session_map[upd]->log_mask;
+ } else {
+ DIAG_LOG(DIAG_DEBUG_MASKS,
+ "asking for mask update with unknown session mask\n");
+ return;
+ }
+ } else {
mask_info = &log_mask;
+ }
if (!mask_info || !mask_info->ptr || !mask_info->update_buf)
return;
@@ -195,7 +210,7 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id)
static void diag_send_event_mask_update(uint8_t peripheral)
{
- uint8_t *buf = NULL;
+ uint8_t *buf = NULL, upd = 0;
uint8_t *temp = NULL;
struct diag_ctrl_event_mask header;
struct diag_mask_info *mask_info = NULL;
@@ -220,11 +235,25 @@ static void diag_send_event_mask_update(uint8_t peripheral)
return;
}
- if (driver->md_session_mask != 0 &&
- (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)))
- mask_info = driver->md_session_map[peripheral]->event_mask;
- else
+ if (driver->md_session_mask != 0) {
+ if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) {
+ if (driver->md_session_map[peripheral])
+ mask_info =
+ driver->md_session_map[peripheral]->event_mask;
+ } else if (driver->md_session_mask &
+ MD_PERIPHERAL_PD_MASK(peripheral)) {
+ upd = diag_mask_to_pd_value(driver->md_session_mask);
+ if (upd && driver->md_session_map[upd])
+ mask_info =
+ driver->md_session_map[upd]->event_mask;
+ } else {
+ DIAG_LOG(DIAG_DEBUG_MASKS,
+ "asking for mask update with unknown session mask\n");
+ return;
+ }
+ } else {
mask_info = &event_mask;
+ }
if (!mask_info || !mask_info->ptr || !mask_info->update_buf)
return;
@@ -284,7 +313,7 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last)
int err = 0;
int header_len = sizeof(struct diag_ctrl_msg_mask);
int temp_len = 0;
- uint8_t *buf = NULL;
+ uint8_t *buf = NULL, upd = 0;
uint8_t *temp = NULL;
uint32_t mask_size = 0;
struct diag_mask_info *mask_info = NULL;
@@ -301,11 +330,25 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last)
return;
}
- if (driver->md_session_mask != 0 &&
- (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)))
- mask_info = driver->md_session_map[peripheral]->msg_mask;
- else
+ if (driver->md_session_mask != 0) {
+ if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) {
+ if (driver->md_session_map[peripheral])
+ mask_info =
+ driver->md_session_map[peripheral]->msg_mask;
+ } else if (driver->md_session_mask &
+ MD_PERIPHERAL_PD_MASK(peripheral)) {
+ upd = diag_mask_to_pd_value(driver->md_session_mask);
+ if (upd && driver->md_session_map[upd])
+ mask_info =
+ driver->md_session_map[upd]->msg_mask;
+ } else {
+ DIAG_LOG(DIAG_DEBUG_MASKS,
+ "asking for mask update with unknown session mask\n");
+ return;
+ }
+ } else {
mask_info = &msg_mask;
+ }
if (!mask_info || !mask_info->ptr || !mask_info->update_buf)
return;
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index a5d92c51cc0b..06b83f5230bf 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -254,8 +254,6 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
struct diag_md_session_t *session_info = NULL;
struct pid *pid_struct = NULL;
- mutex_lock(&driver->diagfwd_untag_mutex);
-
for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
ch = &diag_md[i];
for (j = 0; j < ch->num_tbl_entries && !err; j++) {
@@ -360,17 +358,11 @@ drop_data:
err = copy_to_user(buf + sizeof(int),
(void *)&num_data,
sizeof(int));
- } else {
- DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
- "diag: md_session_map[%d] with pid = %d Exited..\n",
- peripheral, driver->md_session_map[peripheral]->pid);
}
diag_ws_on_copy_complete(DIAG_WS_MUX);
if (drain_again)
chk_logging_wakeup();
- mutex_unlock(&driver->diagfwd_untag_mutex);
-
return err;
}
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 73296b573436..92cf24dcab5e 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -235,6 +235,10 @@
#define MD_PERIPHERAL_MASK(x) (1 << x)
+#define MD_PERIPHERAL_PD_MASK(x) \
+ ((x == PERIPHERAL_MODEM) ? (1 << UPD_WLAN) : \
+ ((x == PERIPHERAL_LPASS) ? (1 << UPD_AUDIO | 1 << UPD_SENSORS) : 0))\
+
/*
* Number of stm processors includes all the peripherals and
* apps.Added 1 below to indicate apps
@@ -543,7 +547,6 @@ struct diagchar_dev {
struct mutex cmd_reg_mutex;
uint32_t cmd_reg_count;
struct mutex diagfwd_channel_mutex[NUM_PERIPHERALS];
- struct mutex diagfwd_untag_mutex;
/* Sizes that reflect memory pool sizes */
unsigned int poolsize;
unsigned int poolsize_hdlc;
@@ -609,12 +612,6 @@ struct diagchar_dev {
int pd_logging_mode[NUM_UPD];
int pd_session_clear[NUM_UPD];
int num_pd_session;
- 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;
@@ -675,6 +672,7 @@ void diag_cmd_remove_reg_by_proc(int proc);
int diag_cmd_chk_polling(struct diag_cmd_reg_entry_t *entry);
int diag_mask_param(void);
void diag_clear_masks(struct diag_md_session_t *info);
+uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask);
void diag_record_stats(int type, int flag);
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 5c1094b48e92..0bc23199b92e 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -403,6 +403,30 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask)
ret |= DIAG_CON_UPD_SENSORS;
return ret;
}
+
+uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask)
+{
+ uint8_t upd = 0;
+ uint32_t pd_mask = 0;
+
+ pd_mask = diag_translate_kernel_to_user_mask(peripheral_mask);
+ switch (pd_mask) {
+ case DIAG_CON_UPD_WLAN:
+ upd = UPD_WLAN;
+ break;
+ case DIAG_CON_UPD_AUDIO:
+ upd = UPD_AUDIO;
+ break;
+ case DIAG_CON_UPD_SENSORS:
+ upd = UPD_SENSORS;
+ break;
+ default:
+ DIAG_LOG(DIAG_DEBUG_MASKS,
+ "asking for mask update with no pd mask set\n");
+ }
+ return upd;
+}
+
int diag_mask_param(void)
{
return diag_mask_clear_param;
@@ -457,20 +481,21 @@ static void diag_close_logging_process(const int pid)
params.req_mode = USB_MODE;
params.mode_param = 0;
+ params.pd_mask = 0;
params.peripheral_mask =
diag_translate_kernel_to_user_mask(session_mask);
- for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
- if (session_mask &
- MD_PERIPHERAL_MASK(i)) {
+ if (driver->num_pd_session > 0) {
+ for (i = UPD_WLAN; ((i < NUM_MD_SESSIONS) &&
+ (session_mask & MD_PERIPHERAL_MASK(i)));
+ 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;
+ }
}
diag_switch_logging(&params);
@@ -1588,7 +1613,7 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask)
static int diag_switch_logging(struct diag_logging_mode_param_t *param)
{
- int new_mode, i;
+ int new_mode, i = 0;
int curr_mode;
int err = 0;
uint8_t do_switch = 1;
@@ -1629,6 +1654,8 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
diag_mux->mux_mask)) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"diag_fr: User PD is already logging onto active peripheral logging\n");
+ i = upd - UPD_WLAN;
+ driver->pd_session_clear[i] = 0;
return -EINVAL;
}
peripheral_mask =
@@ -1638,8 +1665,8 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
if (!driver->pd_session_clear[i]) {
driver->pd_logging_mode[i] = 1;
driver->num_pd_session += 1;
- driver->pd_session_clear[i] = 0;
}
+ driver->pd_session_clear[i] = 0;
} else {
peripheral_mask =
diag_translate_mask(param->peripheral_mask);
@@ -3599,7 +3626,6 @@ static int __init diagchar_init(void)
mutex_init(&driver->msg_mask_lock);
for (i = 0; i < NUM_PERIPHERALS; i++)
mutex_init(&driver->diagfwd_channel_mutex[i]);
- mutex_init(&driver->diagfwd_untag_mutex);
init_waitqueue_head(&driver->wait_q);
INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
INIT_WORK(&(driver->update_user_clients),
diff --git a/drivers/char/diag/diagfwd_glink.h b/drivers/char/diag/diagfwd_glink.h
index a84fa4edfca0..6cad44522ab6 100644
--- a/drivers/char/diag/diagfwd_glink.h
+++ b/drivers/char/diag/diagfwd_glink.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, 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
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index e86dc8292bf0..e209039bed5a 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -363,7 +363,6 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
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 &&
@@ -463,10 +462,10 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
if (peripheral == PERIPHERAL_LPASS &&
fwd_info->type == TYPE_DATA && len_upd_2) {
if (flag_buf_1) {
- driver->upd_len_2_a = len_upd_2;
+ fwd_info->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;
+ fwd_info->upd_len_2_b = len_upd_2;
temp_ptr_upd = fwd_info->buf_upd_2_b;
}
temp_ptr_upd->ctxt &= 0x00FFFFFF;
@@ -477,17 +476,17 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_ptr_upd, len_upd_2);
} else {
if (flag_buf_1)
- driver->upd_len_2_a = 0;
+ fwd_info->upd_len_2_a = 0;
if (flag_buf_2)
- driver->upd_len_2_b = 0;
+ fwd_info->upd_len_2_b = 0;
}
if (fwd_info->type == TYPE_DATA && len_upd_1) {
if (flag_buf_1) {
- driver->upd_len_1_a[peripheral] =
+ fwd_info->upd_len_1_a =
len_upd_1;
temp_ptr_upd = fwd_info->buf_upd_1_a;
} else {
- driver->upd_len_1_b[peripheral] =
+ fwd_info->upd_len_1_b =
len_upd_1;
temp_ptr_upd = fwd_info->buf_upd_1_b;
}
@@ -499,15 +498,15 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_ptr_upd, len_upd_1);
} else {
if (flag_buf_1)
- driver->upd_len_1_a[peripheral] = 0;
+ fwd_info->upd_len_1_a = 0;
if (flag_buf_2)
- driver->upd_len_1_b[peripheral] = 0;
+ fwd_info->upd_len_1_b = 0;
}
if (len_cpd) {
if (flag_buf_1)
- driver->cpd_len_1[peripheral] = len_cpd;
+ fwd_info->cpd_len_1 = len_cpd;
else
- driver->cpd_len_2[peripheral] = len_cpd;
+ fwd_info->cpd_len_2 = len_cpd;
temp_ptr_cpd->ctxt &= 0x00FFFFFF;
temp_ptr_cpd->ctxt |=
(SET_PD_CTXT(ctxt_cpd));
@@ -515,11 +514,10 @@ 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[peripheral] = 0;
+ fwd_info->cpd_len_1 = 0;
if (flag_buf_2)
- driver->cpd_len_2[peripheral] = 0;
+ fwd_info->cpd_len_2 = 0;
}
- mutex_unlock(&driver->diagfwd_untag_mutex);
return;
} else {
diagfwd_data_read_done(fwd_info, buf, len);
@@ -527,7 +525,6 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
}
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));
@@ -759,6 +756,12 @@ int diagfwd_peripheral_init(void)
fwd_info->inited = 1;
fwd_info->read_bytes = 0;
fwd_info->write_bytes = 0;
+ fwd_info->cpd_len_1 = 0;
+ fwd_info->cpd_len_2 = 0;
+ fwd_info->upd_len_1_a = 0;
+ fwd_info->upd_len_1_b = 0;
+ fwd_info->upd_len_2_a = 0;
+ fwd_info->upd_len_2_a = 0;
mutex_init(&fwd_info->buf_mutex);
mutex_init(&fwd_info->data_mutex);
spin_lock_init(&fwd_info->write_buf_lock);
@@ -775,6 +778,12 @@ int diagfwd_peripheral_init(void)
fwd_info->ch_open = 0;
fwd_info->read_bytes = 0;
fwd_info->write_bytes = 0;
+ fwd_info->cpd_len_1 = 0;
+ fwd_info->cpd_len_2 = 0;
+ fwd_info->upd_len_1_a = 0;
+ fwd_info->upd_len_1_b = 0;
+ fwd_info->upd_len_2_a = 0;
+ fwd_info->upd_len_2_a = 0;
spin_lock_init(&fwd_info->write_buf_lock);
mutex_init(&fwd_info->buf_mutex);
mutex_init(&fwd_info->data_mutex);
@@ -1273,11 +1282,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
if (ctxt == 1 && fwd_info->buf_1) {
/* Buffer 1 for core PD is freed */
atomic_set(&fwd_info->buf_1->in_busy, 0);
- driver->cpd_len_1[peripheral] = 0;
+ fwd_info->cpd_len_1 = 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);
- driver->cpd_len_2[peripheral] = 0;
+ fwd_info->cpd_len_2 = 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);
@@ -1286,17 +1295,17 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
/* 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)
+ if (!fwd_info->cpd_len_1 &&
+ !fwd_info->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])
+ if (!fwd_info->cpd_len_1)
atomic_set(&fwd_info->buf_1->in_busy, 0);
}
- driver->upd_len_1_a[peripheral] = 0;
+ fwd_info->upd_len_1_a = 0;
} else if (ctxt == 4 && fwd_info->buf_upd_1_b) {
/* Buffer 2 for user pd 1 is freed */
@@ -1305,17 +1314,17 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
/* 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)
+ if (!fwd_info->cpd_len_2 &&
+ !fwd_info->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])
+ if (!fwd_info->cpd_len_2)
atomic_set(&fwd_info->buf_2->in_busy, 0);
}
- driver->upd_len_1_b[peripheral] = 0;
+ fwd_info->upd_len_1_b = 0;
} else if (ctxt == 5 && fwd_info->buf_upd_2_a) {
/* Buffer 1 for user pd 2 is freed */
@@ -1323,11 +1332,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
/* 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])
+ if (!fwd_info->cpd_len_1 &&
+ !fwd_info->upd_len_1_a)
atomic_set(&fwd_info->buf_1->in_busy, 0);
- driver->upd_len_2_a = 0;
+ fwd_info->upd_len_2_a = 0;
} else if (ctxt == 6 && fwd_info->buf_upd_2_b) {
/* Buffer 2 for user pd 2 is freed */
@@ -1335,11 +1344,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
/* 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])
+ if (!fwd_info->cpd_len_2 &&
+ !fwd_info->upd_len_1_b)
atomic_set(&fwd_info->buf_2->in_busy, 0);
- driver->upd_len_2_b = 0;
+ fwd_info->upd_len_2_b = 0;
} else
pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt);
diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h
index 760f139ff428..037eeebdeb35 100644
--- a/drivers/char/diag/diagfwd_peripheral.h
+++ b/drivers/char/diag/diagfwd_peripheral.h
@@ -83,6 +83,12 @@ struct diagfwd_info {
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];
+ int cpd_len_1;
+ int cpd_len_2;
+ int upd_len_1_a;
+ int upd_len_1_b;
+ int upd_len_2_a;
+ int upd_len_2_b;
struct diag_peripheral_ops *p_ops;
struct diag_channel_ops *c_ops;
};
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 90e624662257..0d83cfb9708f 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -888,6 +888,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
* for details on the intricacies of this.
*/
int left;
+ unsigned char *data_to_send;
ssif_inc_stat(ssif_info, sent_messages_parts);
@@ -896,6 +897,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
left = 32;
/* Length byte. */
ssif_info->multi_data[ssif_info->multi_pos] = left;
+ data_to_send = ssif_info->multi_data + ssif_info->multi_pos;
ssif_info->multi_pos += left;
if (left < 32)
/*
@@ -909,7 +911,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
rv = ssif_i2c_send(ssif_info, msg_written_handler,
I2C_SMBUS_WRITE,
SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE,
- ssif_info->multi_data + ssif_info->multi_pos,
+ data_to_send,
I2C_SMBUS_BLOCK_DATA);
if (rv < 0) {
/* request failed, just return the error. */
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index c4094c4e22c1..34ef474a3923 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -859,7 +859,11 @@ static int __init lp_setup (char *str)
} else if (!strcmp(str, "auto")) {
parport_nr[0] = LP_PARPORT_AUTO;
} else if (!strcmp(str, "none")) {
- parport_nr[parport_ptr++] = LP_PARPORT_NONE;
+ if (parport_ptr < LP_NO)
+ parport_nr[parport_ptr++] = LP_PARPORT_NONE;
+ else
+ printk(KERN_INFO "lp: too many ports, %s ignored.\n",
+ str);
} else if (!strcmp(str, "reset")) {
reset = 1;
}
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index e901463d4972..0975d23031ea 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -343,6 +343,11 @@ static const struct vm_operations_struct mmap_mem_ops = {
static int mmap_mem(struct file *file, struct vm_area_struct *vma)
{
size_t size = vma->vm_end - vma->vm_start;
+ phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
+
+ /* It's illegal to wrap around the end of the physical address space. */
+ if (offset + (phys_addr_t)size < offset)
+ return -EINVAL;
if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
return -EINVAL;
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 2b21398c3adc..35308dfff754 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -118,8 +118,7 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
memcpy_fromio(buf, priv->rsp, 6);
expected = be32_to_cpup((__be32 *) &buf[2]);
-
- if (expected > count)
+ if (expected > count || expected < 6)
return -EIO;
memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6);
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index 7cc1c56a2090..72a75873b810 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -606,6 +606,83 @@ static int clk_osm_acd_auto_local_write_reg(struct clk_osm *c, u32 mask)
return 0;
}
+static int clk_osm_acd_init(struct clk_osm *c)
+{
+
+ int rc = 0;
+ u32 auto_xfer_mask = 0;
+
+ if (!c->acd_init)
+ return 0;
+
+ c->acd_debugfs_addr = ACD_HW_VERSION;
+
+ /* Program ACD tunable-length delay register */
+ clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
+
+ /* Program ACD control register */
+ clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
+
+ /* Program ACD soft start control register */
+ clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
+
+ /* Program initial ACD external interface configuration register */
+ clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
+
+ /* Program ACD auto-register transfer control register */
+ clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL);
+
+ /* Ensure writes complete before transfers to local copy */
+ clk_osm_acd_mb(c);
+
+ /* Transfer master copies */
+ rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
+ if (rc)
+ return rc;
+
+ /* Switch CPUSS clock source to ACD clock */
+ rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
+ ACD_GFMUX_CFG);
+ if (rc)
+ return rc;
+
+ /* Program ACD_DCVS_SW */
+ rc = clk_osm_acd_master_write_through_reg(c,
+ ACD_DCVS_SW_DCVS_IN_PRGR_SET,
+ ACD_DCVS_SW);
+ if (rc)
+ return rc;
+
+ rc = clk_osm_acd_master_write_through_reg(c,
+ ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
+ ACD_DCVS_SW);
+ if (rc)
+ return rc;
+
+ udelay(1);
+
+ /* Program final ACD external interface configuration register */
+ rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
+ ACD_EXTINT_CFG);
+ if (rc)
+ return rc;
+
+ /*
+ * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
+ * must be copied from master to local copy on PC exit.
+ */
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
+ clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG);
+
+ /* ACD has been initialized and enabled for this cluster */
+ c->acd_init = false;
+ return 0;
+}
+
static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec)
{
u64 temp;
@@ -729,6 +806,17 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate)
static int clk_osm_enable(struct clk *c)
{
struct clk_osm *cpuclk = to_clk_osm(c);
+ int rc;
+
+ rc = clk_osm_acd_init(cpuclk);
+ if (rc) {
+ pr_err("Failed to initialize ACD for cluster %d, rc=%d\n",
+ cpuclk->cluster_num, rc);
+ return rc;
+ }
+
+ /* Wait for 5 usecs before enabling OSM */
+ udelay(5);
clk_osm_write_reg(cpuclk, 1, ENABLE_REG);
@@ -3105,81 +3193,6 @@ static int clk_osm_panic_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static int clk_osm_acd_init(struct clk_osm *c)
-{
-
- int rc = 0;
- u32 auto_xfer_mask = 0;
-
- if (!c->acd_init)
- return 0;
-
- c->acd_debugfs_addr = ACD_HW_VERSION;
-
- /* Program ACD tunable-length delay register */
- clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
-
- /* Program ACD control register */
- clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
-
- /* Program ACD soft start control register */
- clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
-
- /* Program initial ACD external interface configuration register */
- clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
-
- /* Program ACD auto-register transfer control register */
- clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL);
-
- /* Ensure writes complete before transfers to local copy */
- clk_osm_acd_mb(c);
-
- /* Transfer master copies */
- rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
- if (rc)
- return rc;
-
- /* Switch CPUSS clock source to ACD clock */
- rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
- ACD_GFMUX_CFG);
- if (rc)
- return rc;
-
- /* Program ACD_DCVS_SW */
- rc = clk_osm_acd_master_write_through_reg(c,
- ACD_DCVS_SW_DCVS_IN_PRGR_SET,
- ACD_DCVS_SW);
- if (rc)
- return rc;
-
- rc = clk_osm_acd_master_write_through_reg(c,
- ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
- ACD_DCVS_SW);
- if (rc)
- return rc;
-
- udelay(1);
-
- /* Program final ACD external interface configuration register */
- rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
- ACD_EXTINT_CFG);
- if (rc)
- return rc;
-
- /*
- * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
- * must be copied from master to local copy on PC exit.
- */
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
- clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG);
-
- return 0;
-}
-
static unsigned long init_rate = 300000000;
static unsigned long osm_clk_init_rate = 200000000;
@@ -3362,17 +3375,6 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
clk_osm_setup_cluster_pll(&perfcl_clk);
}
- rc = clk_osm_acd_init(&pwrcl_clk);
- if (rc) {
- pr_err("failed to initialize ACD for pwrcl, rc=%d\n", rc);
- return rc;
- }
- rc = clk_osm_acd_init(&perfcl_clk);
- if (rc) {
- pr_err("failed to initialize ACD for perfcl, rc=%d\n", rc);
- return rc;
- }
-
spin_lock_init(&pwrcl_clk.lock);
spin_lock_init(&perfcl_clk.lock);
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 4c18181c047c..d3e88f40bdfd 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
clk-qcom-y += clk-branch.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
+clk-qcom-y += clk-regmap-mux-div.o
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o clk-voter.o
diff --git a/drivers/clk/qcom/clk-regmap-mux-div.c b/drivers/clk/qcom/clk-regmap-mux-div.c
new file mode 100644
index 000000000000..9593aefb0bf6
--- /dev/null
+++ b/drivers/clk/qcom/clk-regmap-mux-div.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * 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/bitops.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+
+#include "clk-regmap-mux-div.h"
+
+#define CMD_RCGR 0x0
+#define CMD_RCGR_UPDATE BIT(0)
+#define CMD_RCGR_DIRTY_CFG BIT(4)
+#define CMD_RCGR_ROOT_OFF BIT(31)
+#define CFG_RCGR 0x4
+
+#define to_clk_regmap_mux_div(_hw) \
+ container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
+
+int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div)
+{
+ int ret, count;
+ u32 val, mask;
+ const char *name = clk_hw_get_name(&md->clkr.hw);
+
+ val = (div << md->hid_shift) | (src << md->src_shift);
+ mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
+ ((BIT(md->src_width) - 1) << md->src_shift);
+
+ ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
+ mask, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
+ CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
+ if (ret)
+ return ret;
+
+ /* Wait for update to take effect */
+ for (count = 500; count > 0; count--) {
+ ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
+ &val);
+ if (ret)
+ return ret;
+ if (!(val & CMD_RCGR_UPDATE))
+ return 0;
+ udelay(1);
+ }
+
+ pr_err("%s: RCG did not update its configuration", name);
+ return -EBUSY;
+}
+
+static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
+ u32 *div)
+{
+ u32 val, __div, __src;
+ const char *name = clk_hw_get_name(&md->clkr.hw);
+
+ regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
+
+ if (val & CMD_RCGR_DIRTY_CFG) {
+ pr_err("%s: RCG configuration is pending\n", name);
+ return;
+ }
+
+ regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
+ __src = (val >> md->src_shift);
+ __src &= BIT(md->src_width) - 1;
+ *src = __src;
+
+ __div = (val >> md->hid_shift);
+ __div &= BIT(md->hid_width) - 1;
+ *div = __div;
+}
+
+static int mux_div_enable(struct clk_hw *hw)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ return __mux_div_set_src_div(md, md->src, md->div);
+}
+
+static inline bool is_better_rate(unsigned long req, unsigned long best,
+ unsigned long new)
+{
+ return (req <= new && new < best) || (best < req && best < new);
+}
+
+static int mux_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+ unsigned int i, div, max_div;
+ unsigned long actual_rate, best_rate = 0;
+ unsigned long req_rate = req->rate;
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+
+ max_div = BIT(md->hid_width) - 1;
+ for (div = 1; div < max_div; div++) {
+ parent_rate = mult_frac(req_rate, div, 2);
+ parent_rate = clk_hw_round_rate(parent, parent_rate);
+ actual_rate = mult_frac(parent_rate, 2, div);
+
+ if (is_better_rate(req_rate, best_rate, actual_rate)) {
+ best_rate = actual_rate;
+ req->rate = best_rate;
+ req->best_parent_rate = parent_rate;
+ req->best_parent_hw = parent;
+ }
+
+ if (actual_rate < req_rate || best_rate <= req_rate)
+ break;
+ }
+ }
+
+ if (!best_rate)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate, u32 src)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+ int ret;
+ u32 div, max_div, best_src = 0, best_div = 0;
+ unsigned int i;
+ unsigned long actual_rate, best_rate = 0;
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+
+ max_div = BIT(md->hid_width) - 1;
+ for (div = 1; div < max_div; div++) {
+ parent_rate = mult_frac(rate, div, 2);
+ parent_rate = clk_hw_round_rate(parent, parent_rate);
+ actual_rate = mult_frac(parent_rate, 2, div);
+
+ if (is_better_rate(rate, best_rate, actual_rate)) {
+ best_rate = actual_rate;
+ best_src = md->parent_map[i].cfg;
+ best_div = div - 1;
+ }
+
+ if (actual_rate < rate || best_rate <= rate)
+ break;
+ }
+ }
+
+ ret = __mux_div_set_src_div(md, best_src, best_div);
+ if (!ret) {
+ md->div = best_div;
+ md->src = best_src;
+ }
+
+ return ret;
+}
+
+static u8 mux_div_get_parent(struct clk_hw *hw)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+ const char *name = clk_hw_get_name(hw);
+ u32 i, div, src = 0;
+
+ __mux_div_get_src_div(md, &src, &div);
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++)
+ if (src == md->parent_map[i].cfg)
+ return i;
+
+ pr_err("%s: Can't find parent with src %d\n", name, src);
+ return 0;
+}
+
+static int mux_div_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div);
+}
+
+static int mux_div_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long prate)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ return __mux_div_set_rate_and_parent(hw, rate, prate, md->src);
+}
+
+static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate, u8 index)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ return __mux_div_set_rate_and_parent(hw, rate, prate,
+ md->parent_map[index].cfg);
+}
+
+static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+ u32 div, src;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+ const char *name = clk_hw_get_name(hw);
+
+ __mux_div_get_src_div(md, &src, &div);
+ for (i = 0; i < num_parents; i++)
+ if (src == md->parent_map[i].cfg) {
+ struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
+ unsigned long parent_rate = clk_hw_get_rate(p);
+
+ return mult_frac(parent_rate, 2, div + 1);
+ }
+
+ pr_err("%s: Can't find parent %d\n", name, src);
+ return 0;
+}
+
+static void mux_div_disable(struct clk_hw *hw)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ __mux_div_set_src_div(md, md->safe_src, md->safe_div);
+}
+
+const struct clk_ops clk_regmap_mux_div_ops = {
+ .enable = mux_div_enable,
+ .disable = mux_div_disable,
+ .get_parent = mux_div_get_parent,
+ .set_parent = mux_div_set_parent,
+ .set_rate = mux_div_set_rate,
+ .set_rate_and_parent = mux_div_set_rate_and_parent,
+ .determine_rate = mux_div_determine_rate,
+ .recalc_rate = mux_div_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);
diff --git a/drivers/clk/qcom/clk-regmap-mux-div.h b/drivers/clk/qcom/clk-regmap-mux-div.h
new file mode 100644
index 000000000000..6fac5c54a824
--- /dev/null
+++ b/drivers/clk/qcom/clk-regmap-mux-div.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * 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.
+ */
+
+#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__
+#define __QCOM_CLK_REGMAP_MUX_DIV_H__
+
+#include <linux/clk-provider.h>
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+
+/**
+ * struct mux_div_clk - combined mux/divider clock
+ * @reg_offset: offset of the mux/divider register
+ * @hid_width: number of bits in half integer divider
+ * @hid_shift: lowest bit of hid value field
+ * @src_width: number of bits in source select
+ * @src_shift: lowest bit of source select field
+ * @div: the divider raw configuration value
+ * @src: the mux index which will be used if the clock is enabled
+ * @safe_src: the safe source mux value we switch to, while the main PLL is
+ * reconfigured
+ * @safe_div: the safe divider value that we set, while the main PLL is
+ * reconfigured
+ * @safe_freq: When switching rates from A to B, the mux div clock will
+ * instead switch from A -> safe_freq -> B. This allows the
+ * mux_div clock to change rates while enabled, even if this
+ * behavior is not supported by the parent clocks.
+ * If changing the rate of parent A also causes the rate of
+ * parent B to change, then safe_freq must be defined.
+ * safe_freq is expected to have a source clock which is always
+ * on and runs at only one rate.
+ * @parent_map: pointer to parent_map struct
+ * @clkr: handle between common and hardware-specific interfaces
+ */
+
+struct clk_regmap_mux_div {
+ u32 reg_offset;
+ u32 hid_width;
+ u32 hid_shift;
+ u32 src_width;
+ u32 src_shift;
+ u32 div;
+ u32 src;
+ u32 safe_src;
+ u32 safe_div;
+ unsigned long safe_freq;
+ const struct parent_map *parent_map;
+ struct clk_regmap clkr;
+};
+
+extern const struct clk_ops clk_regmap_mux_div_ops;
+int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div);
+
+#endif
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 893b0b6da6b8..b5c5dc035c66 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -437,6 +437,7 @@ struct qcrypto_cipher_req_ctx {
u8 rfc4309_iv[QCRYPTO_MAX_IV_LENGTH];
unsigned int ivsize;
int aead;
+ int ccmtype; /* default: 0, rfc4309: 1 */
struct scatterlist asg; /* Formatted associated data sg */
unsigned char *adata; /* Pointer to formatted assoc data */
enum qce_cipher_alg_enum alg;
@@ -1897,9 +1898,8 @@ static int aead_ccm_set_msg_len(u8 *block, unsigned int msglen, int csize)
return 0;
}
-static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq)
+static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq, uint32_t assoclen)
{
- struct aead_request *areq = (struct aead_request *) qreq->areq;
unsigned int i = ((unsigned int)qreq->iv[0]) + 1;
memcpy(&qreq->nonce[0] , qreq->iv, qreq->ivsize);
@@ -1908,7 +1908,7 @@ static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq)
* NIST Special Publication 800-38C
*/
qreq->nonce[0] |= (8 * ((qreq->authsize - 2) / 2));
- if (areq->assoclen)
+ if (assoclen)
qreq->nonce[0] |= 64;
if (i > MAX_NONCE)
@@ -2118,24 +2118,31 @@ static int _qcrypto_process_aead(struct crypto_engine *pengine,
qreq.flags = cipher_ctx->flags;
if (qreq.mode == QCE_MODE_CCM) {
+ uint32_t assoclen;
+
if (qreq.dir == QCE_ENCRYPT)
qreq.cryptlen = req->cryptlen;
else
qreq.cryptlen = req->cryptlen -
qreq.authsize;
+
+ /* if rfc4309 ccm, adjust assoclen */
+ assoclen = req->assoclen;
+ if (rctx->ccmtype)
+ assoclen -= 8;
/* Get NONCE */
- ret = qccrypto_set_aead_ccm_nonce(&qreq);
+ ret = qccrypto_set_aead_ccm_nonce(&qreq, assoclen);
if (ret)
return ret;
- if (req->assoclen) {
- rctx->adata = kzalloc((req->assoclen + 0x64),
+ if (assoclen) {
+ rctx->adata = kzalloc((assoclen + 0x64),
GFP_ATOMIC);
if (!rctx->adata)
return -ENOMEM;
/* Format Associated data */
ret = qcrypto_aead_ccm_format_adata(&qreq,
- req->assoclen,
+ assoclen,
req->src,
rctx->adata);
} else {
@@ -2592,6 +2599,7 @@ static int _qcrypto_aead_encrypt_aes_ccm(struct aead_request *req)
rctx->dir = QCE_ENCRYPT;
rctx->mode = QCE_MODE_CCM;
rctx->iv = req->iv;
+ rctx->ccmtype = 0;
pstat->aead_ccm_aes_enc++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2606,6 +2614,8 @@ static int _qcrypto_aead_rfc4309_enc_aes_ccm(struct aead_request *req)
pstat = &_qcrypto_stat;
+ if (req->assoclen != 16 && req->assoclen != 20)
+ return -EINVAL;
rctx = aead_request_ctx(req);
rctx->aead = 1;
rctx->alg = CIPHER_ALG_AES;
@@ -2615,6 +2625,7 @@ static int _qcrypto_aead_rfc4309_enc_aes_ccm(struct aead_request *req)
rctx->rfc4309_iv[0] = 3; /* L -1 */
memcpy(&rctx->rfc4309_iv[1], ctx->ccm4309_nonce, 3);
memcpy(&rctx->rfc4309_iv[4], req->iv, 8);
+ rctx->ccmtype = 1;
rctx->iv = rctx->rfc4309_iv;
pstat->aead_rfc4309_ccm_aes_enc++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2922,6 +2933,7 @@ static int _qcrypto_aead_decrypt_aes_ccm(struct aead_request *req)
rctx->dir = QCE_DECRYPT;
rctx->mode = QCE_MODE_CCM;
rctx->iv = req->iv;
+ rctx->ccmtype = 0;
pstat->aead_ccm_aes_dec++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2935,6 +2947,8 @@ static int _qcrypto_aead_rfc4309_dec_aes_ccm(struct aead_request *req)
struct crypto_stat *pstat;
pstat = &_qcrypto_stat;
+ if (req->assoclen != 16 && req->assoclen != 20)
+ return -EINVAL;
rctx = aead_request_ctx(req);
rctx->aead = 1;
rctx->alg = CIPHER_ALG_AES;
@@ -2944,6 +2958,7 @@ static int _qcrypto_aead_rfc4309_dec_aes_ccm(struct aead_request *req)
rctx->rfc4309_iv[0] = 3; /* L -1 */
memcpy(&rctx->rfc4309_iv[1], ctx->ccm4309_nonce, 3);
memcpy(&rctx->rfc4309_iv[4], req->iv, 8);
+ rctx->ccmtype = 1;
rctx->iv = rctx->rfc4309_iv;
pstat->aead_rfc4309_ccm_aes_dec++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index 5b261adb4b69..3a25da4a6e60 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -1126,23 +1126,10 @@ static u32 dce_v10_0_latency_watermark(struct dce10_wm_params *wm)
a.full = dfixed_const(available_bandwidth);
b.full = dfixed_const(wm->num_heads);
a.full = dfixed_div(a, b);
+ tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512);
+ tmp = min(dfixed_trunc(a), tmp);
- b.full = dfixed_const(mc_latency + 512);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(b, c);
-
- c.full = dfixed_const(dmif_size);
- b.full = dfixed_div(c, b);
-
- tmp = min(dfixed_trunc(a), dfixed_trunc(b));
-
- b.full = dfixed_const(1000);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(c, b);
- c.full = dfixed_const(wm->bytes_per_pixel);
- b.full = dfixed_mul(b, c);
-
- lb_fill_bw = min(tmp, dfixed_trunc(b));
+ lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000);
a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
b.full = dfixed_const(1000);
@@ -1250,14 +1237,14 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev,
{
struct drm_display_mode *mode = &amdgpu_crtc->base.mode;
struct dce10_wm_params wm_low, wm_high;
- u32 pixel_period;
+ u32 active_time;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
- pixel_period = 1000000 / (u32)mode->clock;
- line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+ active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock;
+ line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535);
/* watermark for high clocks */
if (adev->pm.dpm_enabled) {
@@ -1272,7 +1259,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev,
wm_high.disp_clk = mode->clock;
wm_high.src_width = mode->crtc_hdisplay;
- wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_high.active_time = active_time;
wm_high.blank_time = line_time - wm_high.active_time;
wm_high.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -1311,7 +1298,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev,
wm_low.disp_clk = mode->clock;
wm_low.src_width = mode->crtc_hdisplay;
- wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_low.active_time = active_time;
wm_low.blank_time = line_time - wm_low.active_time;
wm_low.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index 267749a94c5a..d6d3cda77762 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -1114,23 +1114,10 @@ static u32 dce_v11_0_latency_watermark(struct dce10_wm_params *wm)
a.full = dfixed_const(available_bandwidth);
b.full = dfixed_const(wm->num_heads);
a.full = dfixed_div(a, b);
+ tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512);
+ tmp = min(dfixed_trunc(a), tmp);
- b.full = dfixed_const(mc_latency + 512);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(b, c);
-
- c.full = dfixed_const(dmif_size);
- b.full = dfixed_div(c, b);
-
- tmp = min(dfixed_trunc(a), dfixed_trunc(b));
-
- b.full = dfixed_const(1000);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(c, b);
- c.full = dfixed_const(wm->bytes_per_pixel);
- b.full = dfixed_mul(b, c);
-
- lb_fill_bw = min(tmp, dfixed_trunc(b));
+ lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000);
a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
b.full = dfixed_const(1000);
@@ -1238,14 +1225,14 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev,
{
struct drm_display_mode *mode = &amdgpu_crtc->base.mode;
struct dce10_wm_params wm_low, wm_high;
- u32 pixel_period;
+ u32 active_time;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
- pixel_period = 1000000 / (u32)mode->clock;
- line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+ active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock;
+ line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535);
/* watermark for high clocks */
if (adev->pm.dpm_enabled) {
@@ -1260,7 +1247,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev,
wm_high.disp_clk = mode->clock;
wm_high.src_width = mode->crtc_hdisplay;
- wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_high.active_time = active_time;
wm_high.blank_time = line_time - wm_high.active_time;
wm_high.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -1299,7 +1286,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev,
wm_low.disp_clk = mode->clock;
wm_low.src_width = mode->crtc_hdisplay;
- wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_low.active_time = active_time;
wm_low.blank_time = line_time - wm_low.active_time;
wm_low.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index 9b4dcf76ce6c..d6e51d4b04f0 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -1096,23 +1096,10 @@ static u32 dce_v8_0_latency_watermark(struct dce8_wm_params *wm)
a.full = dfixed_const(available_bandwidth);
b.full = dfixed_const(wm->num_heads);
a.full = dfixed_div(a, b);
+ tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512);
+ tmp = min(dfixed_trunc(a), tmp);
- b.full = dfixed_const(mc_latency + 512);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(b, c);
-
- c.full = dfixed_const(dmif_size);
- b.full = dfixed_div(c, b);
-
- tmp = min(dfixed_trunc(a), dfixed_trunc(b));
-
- b.full = dfixed_const(1000);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(c, b);
- c.full = dfixed_const(wm->bytes_per_pixel);
- b.full = dfixed_mul(b, c);
-
- lb_fill_bw = min(tmp, dfixed_trunc(b));
+ lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000);
a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
b.full = dfixed_const(1000);
@@ -1220,14 +1207,14 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev,
{
struct drm_display_mode *mode = &amdgpu_crtc->base.mode;
struct dce8_wm_params wm_low, wm_high;
- u32 pixel_period;
+ u32 active_time;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
- pixel_period = 1000000 / (u32)mode->clock;
- line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+ active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock;
+ line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535);
/* watermark for high clocks */
if (adev->pm.dpm_enabled) {
@@ -1242,7 +1229,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev,
wm_high.disp_clk = mode->clock;
wm_high.src_width = mode->crtc_hdisplay;
- wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_high.active_time = active_time;
wm_high.blank_time = line_time - wm_high.active_time;
wm_high.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -1281,7 +1268,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev,
wm_low.disp_clk = mode->clock;
wm_low.src_width = mode->crtc_hdisplay;
- wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_low.active_time = active_time;
wm_low.blank_time = line_time - wm_low.active_time;
wm_low.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 02a60a1df50d..39b8e171cad5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -75,6 +75,8 @@
#define EDID_QUIRK_FORCE_12BPC (1 << 9)
/* Force 6bpc */
#define EDID_QUIRK_FORCE_6BPC (1 << 10)
+/* Force 10bpc */
+#define EDID_QUIRK_FORCE_10BPC (1 << 11)
struct detailed_mode_closure {
struct drm_connector *connector;
@@ -125,6 +127,9 @@ static struct edid_quirk {
{ "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
EDID_QUIRK_DETAILED_IN_CM },
+ /* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */
+ { "LGD", 764, EDID_QUIRK_FORCE_10BPC },
+
/* LG Philips LCD LP154W01-A5 */
{ "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
{ "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
@@ -4478,6 +4483,9 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
if (quirks & EDID_QUIRK_FORCE_8BPC)
connector->display_info.bpc = 8;
+ if (quirks & EDID_QUIRK_FORCE_10BPC)
+ connector->display_info.bpc = 10;
+
if (quirks & EDID_QUIRK_FORCE_12BPC)
connector->display_info.bpc = 12;
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 04de6fd88f8c..6e4dd62d4ed9 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -46,6 +46,8 @@
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/export.h>
+#include <linux/interval_tree_generic.h>
+#include <linux/rbtree.h>
/**
* DOC: Overview
@@ -73,7 +75,8 @@
* allocations and avoiding too much fragmentation. This means free space
* searches are O(num_holes). Given that all the fancy features drm_mm supports
* something better would be fairly complex and since gfx thrashing is a fairly
- * steep cliff not a real concern. Removing a node again is O(1).
+ * steep cliff not a real concern. Removing a node again is O(1). With the
+ * rbtree to track free holes, free hole search becomes O(log(num_holes)).
*
* drm_mm supports a few features: Alignment and range restrictions can be
* supplied. Further more every &drm_mm_node has a color value (which is just an
@@ -103,6 +106,98 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
u64 end,
enum drm_mm_search_flags flags);
+#define START(node) ((node)->start)
+#define LAST(node) ((node)->start + (node)->size - 1)
+
+INTERVAL_TREE_DEFINE(struct drm_mm_node, rb,
+ u64, __subtree_last,
+ START, LAST, static inline, drm_mm_interval_tree)
+
+struct drm_mm_node *
+drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last)
+{
+ return drm_mm_interval_tree_iter_first(&mm->interval_tree,
+ start, last);
+}
+EXPORT_SYMBOL(drm_mm_interval_first);
+
+struct drm_mm_node *
+drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last)
+{
+ return drm_mm_interval_tree_iter_next(node, start, last);
+}
+EXPORT_SYMBOL(drm_mm_interval_next);
+
+static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
+ struct drm_mm_node *node)
+{
+ struct drm_mm *mm = hole_node->mm;
+ struct rb_node **link, *rb;
+ struct drm_mm_node *parent;
+
+ node->__subtree_last = LAST(node);
+
+ if (hole_node->allocated) {
+ rb = &hole_node->rb;
+ while (rb) {
+ parent = rb_entry(rb, struct drm_mm_node, rb);
+ if (parent->__subtree_last >= node->__subtree_last)
+ break;
+
+ parent->__subtree_last = node->__subtree_last;
+ rb = rb_parent(rb);
+ }
+
+ rb = &hole_node->rb;
+ link = &hole_node->rb.rb_right;
+ } else {
+ rb = NULL;
+ link = &mm->interval_tree.rb_node;
+ }
+
+ while (*link) {
+ rb = *link;
+ parent = rb_entry(rb, struct drm_mm_node, rb);
+ if (parent->__subtree_last < node->__subtree_last)
+ parent->__subtree_last = node->__subtree_last;
+ if (node->start < parent->start)
+ link = &parent->rb.rb_left;
+ else
+ link = &parent->rb.rb_right;
+ }
+
+ rb_link_node(&node->rb, rb, link);
+ rb_insert_augmented(&node->rb,
+ &mm->interval_tree,
+ &drm_mm_interval_tree_augment);
+}
+
+static void
+rb_insert_hole_node(struct drm_mm_node *hole_node, struct drm_mm *mm)
+{
+ struct rb_node **new = &(mm->holes_tree.rb_node);
+ struct rb_node *parent = NULL;
+ struct drm_mm_node *cur;
+
+ while (*new) {
+ parent = *new;
+ cur = rb_entry(parent, struct drm_mm_node, hole_node);
+
+ if (__drm_mm_hole_node_start(hole_node)
+ < __drm_mm_hole_node_start(cur))
+ new = &parent->rb_left;
+ else
+ new = &parent->rb_right;
+ }
+ rb_link_node(&hole_node->hole_node, parent, new);
+ rb_insert_color(&hole_node->hole_node, &mm->holes_tree);
+}
+
+static void rb_erase_hole_node(struct drm_mm_node *hole_node, struct drm_mm *mm)
+{
+ rb_erase(&hole_node->hole_node, &mm->holes_tree);
+}
+
static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
struct drm_mm_node *node,
u64 size, unsigned alignment,
@@ -142,6 +237,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
if (adj_start == hole_start) {
hole_node->hole_follows = 0;
list_del(&hole_node->hole_stack);
+ rb_erase_hole_node(hole_node, mm);
}
node->start = adj_start;
@@ -150,14 +246,16 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
node->color = color;
node->allocated = 1;
- INIT_LIST_HEAD(&node->hole_stack);
list_add(&node->node_list, &hole_node->node_list);
+ drm_mm_interval_tree_add_node(hole_node, node);
+
BUG_ON(node->start + node->size > adj_end);
node->hole_follows = 0;
if (__drm_mm_hole_node_start(node) < hole_end) {
list_add(&node->hole_stack, &mm->hole_stack);
+ rb_insert_hole_node(node, mm);
node->hole_follows = 1;
}
}
@@ -178,39 +276,54 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
*/
int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
{
- struct drm_mm_node *hole;
u64 end = node->start + node->size;
- u64 hole_start;
- u64 hole_end;
+ struct drm_mm_node *hole;
+ u64 hole_start, hole_end;
- BUG_ON(node == NULL);
+ if (WARN_ON(node->size == 0))
+ return -EINVAL;
/* Find the relevant hole to add our node to */
- drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
- if (hole_start > node->start || hole_end < end)
- continue;
+ hole = drm_mm_interval_tree_iter_first(&mm->interval_tree,
+ node->start, ~(u64)0);
+ if (hole) {
+ if (hole->start < end)
+ return -ENOSPC;
+ } else {
+ hole = list_entry(&mm->head_node.node_list,
+ typeof(*hole), node_list);
+ }
- node->mm = mm;
- node->allocated = 1;
+ hole = list_last_entry(&hole->node_list, typeof(*hole), node_list);
+ if (!hole->hole_follows)
+ return -ENOSPC;
- INIT_LIST_HEAD(&node->hole_stack);
- list_add(&node->node_list, &hole->node_list);
+ hole_start = __drm_mm_hole_node_start(hole);
+ hole_end = __drm_mm_hole_node_end(hole);
+ if (hole_start > node->start || hole_end < end)
+ return -ENOSPC;
- if (node->start == hole_start) {
- hole->hole_follows = 0;
- list_del_init(&hole->hole_stack);
- }
+ node->mm = mm;
+ node->allocated = 1;
- node->hole_follows = 0;
- if (end != hole_end) {
- list_add(&node->hole_stack, &mm->hole_stack);
- node->hole_follows = 1;
- }
+ list_add(&node->node_list, &hole->node_list);
- return 0;
+ drm_mm_interval_tree_add_node(hole, node);
+
+ if (node->start == hole_start) {
+ hole->hole_follows = 0;
+ list_del(&hole->hole_stack);
+ rb_erase_hole_node(hole, mm);
}
- return -ENOSPC;
+ node->hole_follows = 0;
+ if (end != hole_end) {
+ list_add(&node->hole_stack, &mm->hole_stack);
+ rb_insert_hole_node(node, mm);
+ node->hole_follows = 1;
+ }
+
+ return 0;
}
EXPORT_SYMBOL(drm_mm_reserve_node);
@@ -237,6 +350,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
{
struct drm_mm_node *hole_node;
+ if (WARN_ON(size == 0))
+ return -EINVAL;
+
hole_node = drm_mm_search_free_generic(mm, size, alignment,
color, sflags);
if (!hole_node)
@@ -289,6 +405,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
if (adj_start == hole_start) {
hole_node->hole_follows = 0;
list_del(&hole_node->hole_stack);
+ rb_erase_hole_node(hole_node, mm);
}
node->start = adj_start;
@@ -297,9 +414,10 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
node->color = color;
node->allocated = 1;
- INIT_LIST_HEAD(&node->hole_stack);
list_add(&node->node_list, &hole_node->node_list);
+ drm_mm_interval_tree_add_node(hole_node, node);
+
BUG_ON(node->start < start);
BUG_ON(node->start < adj_start);
BUG_ON(node->start + node->size > adj_end);
@@ -308,6 +426,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
node->hole_follows = 0;
if (__drm_mm_hole_node_start(node) < hole_end) {
list_add(&node->hole_stack, &mm->hole_stack);
+ rb_insert_hole_node(node, mm);
node->hole_follows = 1;
}
}
@@ -338,6 +457,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n
{
struct drm_mm_node *hole_node;
+ if (WARN_ON(size == 0))
+ return -EINVAL;
+
hole_node = drm_mm_search_free_in_range_generic(mm,
size, alignment, color,
start, end, sflags);
@@ -377,6 +499,7 @@ void drm_mm_remove_node(struct drm_mm_node *node)
BUG_ON(__drm_mm_hole_node_start(node) ==
__drm_mm_hole_node_end(node));
list_del(&node->hole_stack);
+ rb_erase_hole_node(node, mm);
} else
BUG_ON(__drm_mm_hole_node_start(node) !=
__drm_mm_hole_node_end(node));
@@ -385,9 +508,11 @@ void drm_mm_remove_node(struct drm_mm_node *node)
if (!prev_node->hole_follows) {
prev_node->hole_follows = 1;
list_add(&prev_node->hole_stack, &mm->hole_stack);
+ rb_insert_hole_node(prev_node, mm);
} else
list_move(&prev_node->hole_stack, &mm->hole_stack);
+ drm_mm_interval_tree_remove(node, &mm->interval_tree);
list_del(&node->node_list);
node->allocated = 0;
}
@@ -410,6 +535,46 @@ static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment)
return end >= start + size;
}
+static struct drm_mm_node *get_first_hole(const struct drm_mm *mm,
+ enum drm_mm_search_flags flags)
+{
+ if (flags & DRM_MM_SEARCH_BOTTOM_UP) {
+ struct rb_node *node = rb_first(&mm->holes_tree);
+
+ return rb_entry(node, struct drm_mm_node, hole_node);
+ } else if (flags & DRM_MM_SEARCH_BELOW) {
+ return list_entry((mm)->hole_stack.prev,
+ struct drm_mm_node, hole_stack);
+ } else {
+ return list_entry((mm)->hole_stack.next,
+ struct drm_mm_node, hole_stack);
+ }
+}
+
+static struct drm_mm_node *get_next_hole(struct drm_mm_node *entry,
+ enum drm_mm_search_flags flags)
+{
+ if (flags & DRM_MM_SEARCH_BOTTOM_UP) {
+ return rb_entry(rb_next(&entry->hole_node),
+ struct drm_mm_node, hole_node);
+ } else if (flags & DRM_MM_SEARCH_BELOW) {
+ return list_entry(entry->hole_stack.prev,
+ struct drm_mm_node, hole_stack);
+ } else {
+ return list_entry(entry->hole_stack.next,
+ struct drm_mm_node, hole_stack);
+ }
+}
+
+static bool drm_mm_hole_traversal_condition(const struct drm_mm *mm,
+ struct drm_mm_node *entry, enum drm_mm_search_flags flags)
+{
+ if (flags & DRM_MM_SEARCH_BOTTOM_UP)
+ return entry ? 1 : 0;
+ else
+ return (&entry->hole_stack != &(mm)->hole_stack) ? 1 : 0;
+}
+
static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
u64 size,
unsigned alignment,
@@ -427,9 +592,14 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
best = NULL;
best_size = ~0UL;
- __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
- flags & DRM_MM_SEARCH_BELOW) {
- u64 hole_size = adj_end - adj_start;
+ for (entry = get_first_hole(mm, flags);
+ drm_mm_hole_traversal_condition(mm, entry, flags);
+ entry = get_next_hole(entry, flags)) {
+ u64 hole_size;
+
+ adj_start = drm_mm_hole_node_start(entry);
+ adj_end = drm_mm_hole_node_end(entry);
+ hole_size = adj_end - adj_start;
if (mm->color_adjust) {
mm->color_adjust(entry, color, &adj_start, &adj_end);
@@ -471,9 +641,14 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
best = NULL;
best_size = ~0UL;
- __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
- flags & DRM_MM_SEARCH_BELOW) {
- u64 hole_size = adj_end - adj_start;
+ for (entry = get_first_hole(mm, flags);
+ drm_mm_hole_traversal_condition(mm, entry, flags);
+ entry = get_next_hole(entry, flags)) {
+ u64 hole_size;
+
+ adj_start = drm_mm_hole_node_start(entry);
+ adj_end = drm_mm_hole_node_end(entry);
+ hole_size = adj_end - adj_start;
if (adj_start < start)
adj_start = start;
@@ -514,14 +689,21 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
{
list_replace(&old->node_list, &new->node_list);
list_replace(&old->hole_stack, &new->hole_stack);
+ rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree);
new->hole_follows = old->hole_follows;
new->mm = old->mm;
new->start = old->start;
new->size = old->size;
new->color = old->color;
+ new->__subtree_last = old->__subtree_last;
old->allocated = 0;
new->allocated = 1;
+
+ if (old->hole_follows)
+ rb_replace_node(&old->hole_node, &new->hole_node,
+ &old->mm->holes_tree);
+
}
EXPORT_SYMBOL(drm_mm_replace_node);
@@ -746,7 +928,6 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
/* Clever trick to avoid a special case in the free hole tracking. */
INIT_LIST_HEAD(&mm->head_node.node_list);
- INIT_LIST_HEAD(&mm->head_node.hole_stack);
mm->head_node.hole_follows = 1;
mm->head_node.scanned_block = 0;
mm->head_node.scanned_prev_free = 0;
@@ -756,7 +937,10 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
mm->head_node.size = start - mm->head_node.start;
list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
+ mm->interval_tree = RB_ROOT;
mm->color_adjust = NULL;
+ mm->holes_tree = RB_ROOT;
+ rb_insert_hole_node(&mm->head_node, mm);
}
EXPORT_SYMBOL(drm_mm_init);
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 5838545468f8..dbc198b00792 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -12,6 +12,8 @@ config DRM_MSM
select QCOM_SCM
select BACKLIGHT_CLASS_DEVICE
select MSM_EXT_DISPLAY
+ select MMU_NOTIFIER
+ select INTERVAL_TREE
default y
help
DRM/KMS driver for MSM/snapdragon.
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 245d8911033d..999d5e45e5c5 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -148,6 +148,7 @@ msm_drm-$(CONFIG_DRM_MSM) += \
msm_rd.o \
msm_ringbuffer.o \
msm_prop.o \
- msm_snapshot.o
+ msm_snapshot.o \
+ msm_submitqueue.o
obj-$(CONFIG_DRM_MSM) += msm_drm.o
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 0d8fe805828c..47bee42c59fa 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -51,6 +51,9 @@ static LIST_HEAD(sde_hdmi_list);
#define HDMI_SCDC_ERR_DET_2_H 0x55
#define HDMI_SCDC_ERR_DET_CHECKSUM 0x56
+#define HDMI_DISPLAY_MAX_WIDTH 4096
+#define HDMI_DISPLAY_MAX_HEIGHT 2160
+
static const struct of_device_id sde_hdmi_dt_match[] = {
{.compatible = "qcom,hdmi-display"},
{}
@@ -1524,8 +1527,8 @@ int sde_hdmi_get_info(struct msm_display_info *info,
MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_VID_MODE;
}
info->is_connected = hdmi_display->connected;
- info->max_width = 4096;
- info->max_height = 2160;
+ info->max_width = HDMI_DISPLAY_MAX_WIDTH;
+ info->max_height = HDMI_DISPLAY_MAX_HEIGHT;
info->compression = MSM_DISPLAY_COMPRESS_NONE;
mutex_unlock(&hdmi_display->display_lock);
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
index 0956617442af..ea485a2ec2cd 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
@@ -466,13 +466,13 @@ static inline uint32_t HDMI_DDC_REF_REFTIMER(uint32_t val)
#define REG_HDMI_CEC_RD_FILTER 0x000002b0
#define REG_HDMI_ACTIVE_HSYNC 0x000002b4
-#define HDMI_ACTIVE_HSYNC_START__MASK 0x00000fff
+#define HDMI_ACTIVE_HSYNC_START__MASK 0x00001fff
#define HDMI_ACTIVE_HSYNC_START__SHIFT 0
static inline uint32_t HDMI_ACTIVE_HSYNC_START(uint32_t val)
{
return ((val) << HDMI_ACTIVE_HSYNC_START__SHIFT) & HDMI_ACTIVE_HSYNC_START__MASK;
}
-#define HDMI_ACTIVE_HSYNC_END__MASK 0x0fff0000
+#define HDMI_ACTIVE_HSYNC_END__MASK 0x1fff0000
#define HDMI_ACTIVE_HSYNC_END__SHIFT 16
static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val)
{
@@ -480,13 +480,13 @@ static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val)
}
#define REG_HDMI_ACTIVE_VSYNC 0x000002b8
-#define HDMI_ACTIVE_VSYNC_START__MASK 0x00000fff
+#define HDMI_ACTIVE_VSYNC_START__MASK 0x00001fff
#define HDMI_ACTIVE_VSYNC_START__SHIFT 0
static inline uint32_t HDMI_ACTIVE_VSYNC_START(uint32_t val)
{
return ((val) << HDMI_ACTIVE_VSYNC_START__SHIFT) & HDMI_ACTIVE_VSYNC_START__MASK;
}
-#define HDMI_ACTIVE_VSYNC_END__MASK 0x0fff0000
+#define HDMI_ACTIVE_VSYNC_END__MASK 0x1fff0000
#define HDMI_ACTIVE_VSYNC_END__SHIFT 16
static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val)
{
@@ -494,13 +494,13 @@ static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val)
}
#define REG_HDMI_VSYNC_ACTIVE_F2 0x000002bc
-#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00000fff
+#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00001fff
#define HDMI_VSYNC_ACTIVE_F2_START__SHIFT 0
static inline uint32_t HDMI_VSYNC_ACTIVE_F2_START(uint32_t val)
{
return ((val) << HDMI_VSYNC_ACTIVE_F2_START__SHIFT) & HDMI_VSYNC_ACTIVE_F2_START__MASK;
}
-#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x0fff0000
+#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x1fff0000
#define HDMI_VSYNC_ACTIVE_F2_END__SHIFT 16
static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val)
{
@@ -508,13 +508,13 @@ static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val)
}
#define REG_HDMI_TOTAL 0x000002c0
-#define HDMI_TOTAL_H_TOTAL__MASK 0x00000fff
+#define HDMI_TOTAL_H_TOTAL__MASK 0x00001fff
#define HDMI_TOTAL_H_TOTAL__SHIFT 0
static inline uint32_t HDMI_TOTAL_H_TOTAL(uint32_t val)
{
return ((val) << HDMI_TOTAL_H_TOTAL__SHIFT) & HDMI_TOTAL_H_TOTAL__MASK;
}
-#define HDMI_TOTAL_V_TOTAL__MASK 0x0fff0000
+#define HDMI_TOTAL_V_TOTAL__MASK 0x1fff0000
#define HDMI_TOTAL_V_TOTAL__SHIFT 16
static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val)
{
@@ -522,7 +522,7 @@ static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val)
}
#define REG_HDMI_VSYNC_TOTAL_F2 0x000002c4
-#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00000fff
+#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00001fff
#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__SHIFT 0
static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val)
{
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index b9503564cdd6..02a20462121d 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -391,6 +391,8 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
INIT_LIST_HEAD(&priv->vblank_ctrl.event_list);
init_kthread_work(&priv->vblank_ctrl.work, vblank_ctrl_worker);
spin_lock_init(&priv->vblank_ctrl.lock);
+ hash_init(priv->mn_hash);
+ mutex_init(&priv->mn_lock);
drm_mode_config_init(dev);
@@ -559,7 +561,8 @@ static struct msm_file_private *setup_pagetable(struct msm_drm_private *priv)
return ERR_PTR(-ENOMEM);
ctx->aspace = msm_gem_address_space_create_instance(
- priv->gpu->aspace->mmu, "gpu", 0x100000000, 0x1ffffffff);
+ priv->gpu->aspace->mmu, "gpu", 0x100000000ULL,
+ TASK_SIZE_64 - 1);
if (IS_ERR(ctx->aspace)) {
int ret = PTR_ERR(ctx->aspace);
@@ -601,6 +604,8 @@ static int msm_open(struct drm_device *dev, struct drm_file *file)
INIT_LIST_HEAD(&ctx->counters);
+ msm_submitqueue_init(ctx);
+
file->driver_priv = ctx;
kms = priv->kms;
@@ -629,12 +634,18 @@ static void msm_postclose(struct drm_device *dev, struct drm_file *file)
if (kms && kms->funcs && kms->funcs->postclose)
kms->funcs->postclose(kms, file);
- if (priv->gpu)
+ if (!ctx)
+ return;
+
+ msm_submitqueue_close(ctx);
+
+ if (priv->gpu) {
msm_gpu_cleanup_counters(priv->gpu, ctx);
- if (ctx && ctx->aspace && ctx->aspace != priv->gpu->aspace) {
- ctx->aspace->mmu->funcs->detach(ctx->aspace->mmu);
- msm_gem_address_space_put(ctx->aspace);
+ if (ctx->aspace && ctx->aspace != priv->gpu->aspace) {
+ ctx->aspace->mmu->funcs->detach(ctx->aspace->mmu);
+ msm_gem_address_space_put(ctx->aspace);
+ }
}
kfree(ctx);
@@ -1141,6 +1152,20 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data,
args->flags, &args->handle);
}
+static int msm_ioctl_gem_svm_new(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_msm_gem_svm_new *args = data;
+
+ if (args->flags & ~MSM_BO_FLAGS) {
+ DRM_ERROR("invalid flags: %08x\n", args->flags);
+ return -EINVAL;
+ }
+
+ return msm_gem_svm_new_handle(dev, file, args->hostptr, args->size,
+ args->flags, &args->handle);
+}
+
static inline ktime_t to_ktime(struct drm_msm_timespec timeout)
{
return ktime_set(timeout.tv_sec, timeout.tv_nsec);
@@ -1193,6 +1218,7 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
{
struct drm_msm_gem_info *args = data;
struct drm_gem_object *obj;
+ struct msm_gem_object *msm_obj;
struct msm_file_private *ctx = file->driver_priv;
int ret = 0;
@@ -1203,10 +1229,10 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
if (!obj)
return -ENOENT;
+ msm_obj = to_msm_bo(obj);
if (args->flags & MSM_INFO_IOVA) {
struct msm_gem_address_space *aspace = NULL;
struct msm_drm_private *priv = dev->dev_private;
- struct msm_gem_object *msm_obj = to_msm_bo(obj);
uint64_t iova;
if (msm_obj->flags & MSM_BO_SECURE && priv->gpu)
@@ -1223,6 +1249,14 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
if (!ret)
args->offset = iova;
} else {
+ if (msm_obj->flags & MSM_BO_SVM) {
+ /*
+ * Offset for an SVM object is not needed as they are
+ * already mmap'ed before the SVM ioctl is invoked.
+ */
+ ret = -EACCES;
+ goto out;
+ }
args->offset = msm_gem_mmap_offset(obj);
}
@@ -1657,6 +1691,34 @@ static int msm_ioctl_counter_read(struct drm_device *dev, void *data,
return -ENODEV;
}
+
+static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_msm_submitqueue *args = data;
+ struct msm_drm_private *priv = dev->dev_private;
+ struct msm_gpu *gpu = priv->gpu;
+
+ if (args->flags & ~MSM_SUBMITQUEUE_FLAGS)
+ return -EINVAL;
+
+ if (!file->is_master && args->prio >= gpu->nr_rings - 1) {
+ DRM_ERROR("Only DRM master can set highest priority ringbuffer\n");
+ return -EPERM;
+ }
+
+ return msm_submitqueue_create(file->driver_priv, args->prio,
+ args->flags, &args->id);
+}
+
+static int msm_ioctl_submitqueue_close(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_msm_submitqueue *args = data;
+
+ return msm_submitqueue_remove(file->driver_priv, args->id);
+}
+
int msm_release(struct inode *inode, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
@@ -1700,6 +1762,12 @@ static const struct drm_ioctl_desc msm_ioctls[] = {
DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_GEM_SYNC, msm_ioctl_gem_sync,
DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_GEM_SVM_NEW, msm_ioctl_gem_svm_new,
+ DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new,
+ DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close,
+ DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct vm_operations_struct vm_ops = {
@@ -1968,6 +2036,7 @@ void __exit adreno_unregister(void)
static int __init msm_drm_register(void)
{
DBG("init");
+ msm_smmu_driver_init();
msm_dsi_register();
msm_edp_register();
hdmi_register();
@@ -1983,6 +2052,7 @@ static void __exit msm_drm_unregister(void)
adreno_unregister();
msm_edp_unregister();
msm_dsi_unregister();
+ msm_smmu_driver_cleanup();
}
module_init(msm_drm_register);
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 54a3568ca11f..660c608b64f0 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -34,6 +34,7 @@
#include <linux/of_graph.h>
#include <linux/of_device.h>
#include <linux/sde_io_util.h>
+#include <linux/hashtable.h>
#include <asm/sizes.h>
#include <linux/kthread.h>
@@ -77,6 +78,9 @@ struct msm_gem_vma;
struct msm_file_private {
struct msm_gem_address_space *aspace;
struct list_head counters;
+ rwlock_t queuelock;
+ struct list_head submitqueues;
+ int queueid;
};
enum msm_mdp_plane_property {
@@ -111,6 +115,7 @@ enum msm_mdp_plane_property {
PLANE_PROP_ROTATION,
PLANE_PROP_BLEND_OP,
PLANE_PROP_SRC_CONFIG,
+ PLANE_PROP_FB_TRANSLATION_MODE,
/* total # of properties */
PLANE_PROP_COUNT
@@ -129,6 +134,7 @@ enum msm_mdp_crtc_property {
CRTC_PROP_CORE_CLK,
CRTC_PROP_CORE_AB,
CRTC_PROP_CORE_IB,
+ CRTC_PROP_SECURITY_LEVEL,
/* total # of properties */
CRTC_PROP_COUNT
@@ -325,6 +331,11 @@ struct msm_drm_private {
unsigned int num_connectors;
struct drm_connector *connectors[MAX_CONNECTORS];
+ /* hash to store mm_struct to msm_mmu_notifier mappings */
+ DECLARE_HASHTABLE(mn_hash, 7);
+ /* protects mn_hash and the msm_mmu_notifier for the process */
+ struct mutex mn_lock;
+
/* Properties */
struct drm_property *plane_property[PLANE_PROP_COUNT];
struct drm_property *crtc_property[CRTC_PROP_COUNT];
@@ -404,10 +415,15 @@ void msm_update_fence(struct drm_device *dev, uint32_t fence);
void msm_gem_unmap_vma(struct msm_gem_address_space *aspace,
struct msm_gem_vma *vma, struct sg_table *sgt,
- void *priv);
+ void *priv, bool invalidated);
int msm_gem_map_vma(struct msm_gem_address_space *aspace,
struct msm_gem_vma *vma, struct sg_table *sgt,
void *priv, unsigned int flags);
+int msm_gem_reserve_iova(struct msm_gem_address_space *aspace,
+ struct msm_gem_vma *domain,
+ uint64_t hostptr, uint64_t size);
+void msm_gem_release_iova(struct msm_gem_address_space *aspace,
+ struct msm_gem_vma *vma);
void msm_gem_address_space_put(struct msm_gem_address_space *aspace);
@@ -424,6 +440,7 @@ struct msm_gem_address_space *
msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu,
const char *name);
+void msm_gem_submit_free(struct msm_gem_submit *submit);
int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
struct drm_file *file);
@@ -471,6 +488,12 @@ struct drm_gem_object *msm_gem_new_locked(struct drm_device *dev,
struct drm_gem_object *msm_gem_import(struct drm_device *dev,
uint32_t size, struct sg_table *sgt, u32 flags);
void msm_gem_sync(struct drm_gem_object *obj, u32 op);
+int msm_gem_svm_new_handle(struct drm_device *dev, struct drm_file *file,
+ uint64_t hostptr, uint64_t size,
+ uint32_t flags, uint32_t *handle);
+struct drm_gem_object *msm_gem_svm_new(struct drm_device *dev,
+ struct drm_file *file, uint64_t hostptr,
+ uint64_t size, uint32_t flags);
int msm_framebuffer_prepare(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace);
@@ -487,6 +510,16 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev);
+struct msm_gpu_submitqueue;
+int msm_submitqueue_init(struct msm_file_private *ctx);
+struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
+ u32 id);
+int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio,
+ u32 flags, u32 *id);
+int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id);
+void msm_submitqueue_close(struct msm_file_private *ctx);
+void msm_submitqueue_destroy(struct kref *kref);
+
struct hdmi;
int hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev,
struct drm_encoder *encoder);
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 4b1a586d474d..b7dd84cb23b9 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -25,6 +25,129 @@
#include "msm_gpu.h"
#include "msm_mmu.h"
+static void msm_gem_mn_free(struct kref *refcount)
+{
+ struct msm_mmu_notifier *msm_mn = container_of(refcount,
+ struct msm_mmu_notifier, refcount);
+
+ mmu_notifier_unregister(&msm_mn->mn, msm_mn->mm);
+ hash_del(&msm_mn->node);
+
+ kfree(msm_mn);
+}
+
+static int msm_gem_mn_get(struct msm_mmu_notifier *msm_mn)
+{
+ if (msm_mn)
+ return kref_get_unless_zero(&msm_mn->refcount);
+ return 0;
+}
+
+static void msm_gem_mn_put(struct msm_mmu_notifier *msm_mn)
+{
+ if (msm_mn) {
+ struct msm_drm_private *msm_dev = msm_mn->msm_dev;
+
+ mutex_lock(&msm_dev->mn_lock);
+ kref_put(&msm_mn->refcount, msm_gem_mn_free);
+ mutex_unlock(&msm_dev->mn_lock);
+ }
+}
+
+void msm_mn_invalidate_range_start(struct mmu_notifier *mn,
+ struct mm_struct *mm, unsigned long start, unsigned long end);
+
+static const struct mmu_notifier_ops msm_mn_ops = {
+ .invalidate_range_start = msm_mn_invalidate_range_start,
+};
+
+static struct msm_mmu_notifier *
+msm_gem_mn_find(struct msm_drm_private *msm_dev, struct mm_struct *mm,
+ struct msm_gem_address_space *aspace)
+{
+ struct msm_mmu_notifier *msm_mn;
+ int ret = 0;
+
+ mutex_lock(&msm_dev->mn_lock);
+ hash_for_each_possible(msm_dev->mn_hash, msm_mn, node,
+ (unsigned long) mm) {
+ if (msm_mn->mm == mm) {
+ if (!msm_gem_mn_get(msm_mn)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ mutex_unlock(&msm_dev->mn_lock);
+ return msm_mn;
+ }
+ }
+
+ msm_mn = kzalloc(sizeof(*msm_mn), GFP_KERNEL);
+ if (!msm_mn) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ msm_mn->mm = current->mm;
+ msm_mn->mn.ops = &msm_mn_ops;
+ ret = mmu_notifier_register(&msm_mn->mn, msm_mn->mm);
+ if (ret) {
+ kfree(msm_mn);
+ goto fail;
+ }
+
+ msm_mn->svm_tree = RB_ROOT;
+ spin_lock_init(&msm_mn->svm_tree_lock);
+ kref_init(&msm_mn->refcount);
+ msm_mn->msm_dev = msm_dev;
+
+ /* Insert the msm_mn into the hash */
+ hash_add(msm_dev->mn_hash, &msm_mn->node, (unsigned long) msm_mn->mm);
+ mutex_unlock(&msm_dev->mn_lock);
+
+ return msm_mn;
+
+fail:
+ mutex_unlock(&msm_dev->mn_lock);
+ return ERR_PTR(ret);
+}
+
+static int msm_gem_mn_register(struct msm_gem_svm_object *msm_svm_obj,
+ struct msm_gem_address_space *aspace)
+{
+ struct drm_gem_object *obj = &msm_svm_obj->msm_obj_base.base;
+ struct msm_drm_private *msm_dev = obj->dev->dev_private;
+ struct msm_mmu_notifier *msm_mn;
+
+ msm_svm_obj->mm = current->mm;
+ msm_svm_obj->svm_node.start = msm_svm_obj->hostptr;
+ msm_svm_obj->svm_node.last = msm_svm_obj->hostptr + obj->size - 1;
+
+ msm_mn = msm_gem_mn_find(msm_dev, msm_svm_obj->mm, aspace);
+ if (IS_ERR(msm_mn))
+ return PTR_ERR(msm_mn);
+
+ msm_svm_obj->msm_mn = msm_mn;
+
+ spin_lock(&msm_mn->svm_tree_lock);
+ interval_tree_insert(&msm_svm_obj->svm_node, &msm_mn->svm_tree);
+ spin_unlock(&msm_mn->svm_tree_lock);
+
+ return 0;
+}
+
+static void msm_gem_mn_unregister(struct msm_gem_svm_object *msm_svm_obj)
+{
+ struct msm_mmu_notifier *msm_mn = msm_svm_obj->msm_mn;
+
+ /* invalid: bo already unregistered */
+ if (!msm_mn || msm_svm_obj->invalid)
+ return;
+
+ spin_lock(&msm_mn->svm_tree_lock);
+ interval_tree_remove(&msm_svm_obj->svm_node, &msm_mn->svm_tree);
+ spin_unlock(&msm_mn->svm_tree_lock);
+}
+
static int protect_pages(struct msm_gem_object *msm_obj)
{
int perm = PERM_READ | PERM_WRITE;
@@ -175,10 +298,19 @@ static void put_pages(struct drm_gem_object *obj)
sg_free_table(msm_obj->sgt);
kfree(msm_obj->sgt);
- if (use_pages(obj))
- drm_gem_put_pages(obj, msm_obj->pages, true, false);
- else
+ if (use_pages(obj)) {
+ if (msm_obj->flags & MSM_BO_SVM) {
+ int npages = obj->size >> PAGE_SHIFT;
+
+ release_pages(msm_obj->pages, npages, 0);
+ kfree(msm_obj->pages);
+ } else {
+ drm_gem_put_pages(obj, msm_obj->pages,
+ true, false);
+ }
+ } else {
put_pages_vram(obj);
+ }
msm_obj->pages = NULL;
}
@@ -205,8 +337,8 @@ int msm_gem_mmap_obj(struct drm_gem_object *obj,
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
- /* We can't mmap secure objects */
- if (msm_obj->flags & MSM_BO_SECURE) {
+ /* We can't mmap secure objects or SVM objects */
+ if (msm_obj->flags & (MSM_BO_SECURE | MSM_BO_SVM)) {
drm_gem_vm_close(vma);
return -EACCES;
}
@@ -348,14 +480,21 @@ static void
put_iova(struct drm_gem_object *obj)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
+ struct msm_gem_svm_object *msm_svm_obj;
struct msm_gem_vma *domain, *tmp;
+ bool invalid = false;
WARN_ON(!mutex_is_locked(&msm_obj->lock));
+ if (msm_obj->flags & MSM_BO_SVM) {
+ msm_svm_obj = to_msm_svm_obj(msm_obj);
+ invalid = msm_svm_obj->invalid;
+ }
+
list_for_each_entry_safe(domain, tmp, &msm_obj->domains, list) {
if (iommu_present(&platform_bus_type)) {
msm_gem_unmap_vma(domain->aspace, domain,
- msm_obj->sgt, get_dmabuf_ptr(obj));
+ msm_obj->sgt, get_dmabuf_ptr(obj), invalid);
}
obj_remove_domain(domain);
@@ -514,7 +653,15 @@ void *msm_gem_vaddr(struct drm_gem_object *obj)
struct msm_gem_object *msm_obj = to_msm_bo(obj);
mutex_lock(&msm_obj->lock);
- if (!msm_obj->vaddr) {
+
+ if (msm_obj->vaddr) {
+ mutex_unlock(&msm_obj->lock);
+ return msm_obj->vaddr;
+ }
+
+ if (obj->import_attach) {
+ msm_obj->vaddr = dma_buf_vmap(obj->import_attach->dmabuf);
+ } else {
struct page **pages = get_pages(obj);
if (IS_ERR(pages)) {
mutex_unlock(&msm_obj->lock);
@@ -658,15 +805,26 @@ void msm_gem_free_object(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
struct msm_gem_object *msm_obj = to_msm_bo(obj);
+ struct msm_gem_svm_object *msm_svm_obj = NULL;
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
/* object should not be on active list: */
WARN_ON(is_active(msm_obj));
+
+ if (msm_obj->flags & MSM_BO_SVM)
+ msm_svm_obj = to_msm_svm_obj(msm_obj);
+
list_del(&msm_obj->mm_list);
- mutex_lock(&msm_obj->lock);
+ /* Unregister SVM object from mmu notifications */
+ if (msm_obj->flags & MSM_BO_SVM) {
+ msm_gem_mn_unregister(msm_svm_obj);
+ msm_gem_mn_put(msm_svm_obj->msm_mn);
+ msm_svm_obj->msm_mn = NULL;
+ }
+ mutex_lock(&msm_obj->lock);
put_iova(obj);
if (obj->import_attach) {
@@ -691,7 +849,10 @@ void msm_gem_free_object(struct drm_gem_object *obj)
drm_gem_object_release(obj);
mutex_unlock(&msm_obj->lock);
- kfree(msm_obj);
+ if (msm_obj->flags & MSM_BO_SVM)
+ kfree(msm_svm_obj);
+ else
+ kfree(msm_obj);
}
/* convenience method to construct a GEM buffer object, and userspace handle */
@@ -714,26 +875,32 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
return ret;
}
-static inline void msm_gem_add_to_inactive_list(struct msm_gem_object *msm_obj,
- struct drm_device *dev, bool struct_mutex_locked)
+/* convenience method to construct an SVM buffer object, and userspace handle */
+int msm_gem_svm_new_handle(struct drm_device *dev, struct drm_file *file,
+ uint64_t hostptr, uint64_t size,
+ uint32_t flags, uint32_t *handle)
{
- struct msm_drm_private *priv = dev->dev_private;
+ struct drm_gem_object *obj;
+ int ret;
- if (struct_mutex_locked) {
- list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
- } else {
- mutex_lock(&dev->struct_mutex);
- list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
- mutex_unlock(&dev->struct_mutex);
- }
+ obj = msm_gem_svm_new(dev, file, hostptr, size, flags);
+
+ if (IS_ERR(obj))
+ return PTR_ERR(obj);
+
+ ret = drm_gem_handle_create(file, obj, handle);
+
+ /* drop reference from allocate - handle holds it now */
+ drm_gem_object_unreference_unlocked(obj);
+
+ return ret;
}
-static int msm_gem_new_impl(struct drm_device *dev,
- uint32_t size, uint32_t flags, struct drm_gem_object **obj,
- bool struct_mutex_locked)
+static int msm_gem_obj_init(struct drm_device *dev,
+ uint32_t size, uint32_t flags,
+ struct msm_gem_object *msm_obj, bool struct_mutex_locked)
{
struct msm_drm_private *priv = dev->dev_private;
- struct msm_gem_object *msm_obj;
bool use_vram = false;
switch (flags & MSM_BO_CACHE_MASK) {
@@ -755,10 +922,6 @@ static int msm_gem_new_impl(struct drm_device *dev,
if (WARN_ON(use_vram && !priv->vram.size))
return -EINVAL;
- msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL);
- if (!msm_obj)
- return -ENOMEM;
-
mutex_init(&msm_obj->lock);
if (use_vram) {
@@ -773,20 +936,44 @@ static int msm_gem_new_impl(struct drm_device *dev,
msm_obj->resv = &msm_obj->_resv;
reservation_object_init(msm_obj->resv);
+ INIT_LIST_HEAD(&msm_obj->mm_list);
INIT_LIST_HEAD(&msm_obj->submit_entry);
INIT_LIST_HEAD(&msm_obj->domains);
- msm_gem_add_to_inactive_list(msm_obj, dev, struct_mutex_locked);
-
- *obj = &msm_obj->base;
+ if (struct_mutex_locked) {
+ list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
+ } else {
+ mutex_lock(&dev->struct_mutex);
+ list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
+ mutex_unlock(&dev->struct_mutex);
+ }
return 0;
}
+static struct drm_gem_object *msm_gem_new_impl(struct drm_device *dev,
+ uint32_t size, uint32_t flags, bool struct_mutex_locked)
+{
+ struct msm_gem_object *msm_obj;
+ int ret;
+
+ msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL);
+ if (!msm_obj)
+ return ERR_PTR(-ENOMEM);
+
+ ret = msm_gem_obj_init(dev, size, flags, msm_obj, struct_mutex_locked);
+ if (ret) {
+ kfree(msm_obj);
+ return ERR_PTR(ret);
+ }
+
+ return &msm_obj->base;
+}
+
static struct drm_gem_object *_msm_gem_new(struct drm_device *dev,
uint32_t size, uint32_t flags, bool struct_mutex_locked)
{
- struct drm_gem_object *obj = NULL;
+ struct drm_gem_object *obj;
int ret;
size = PAGE_ALIGN(size);
@@ -798,9 +985,9 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev,
if (!size)
return ERR_PTR(-EINVAL);
- ret = msm_gem_new_impl(dev, size, flags, &obj, struct_mutex_locked);
- if (ret)
- goto fail;
+ obj = msm_gem_new_impl(dev, size, flags, struct_mutex_locked);
+ if (IS_ERR(obj))
+ return obj;
if (use_pages(obj)) {
ret = drm_gem_object_init(dev, obj, size);
@@ -813,8 +1000,7 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev,
return obj;
fail:
- if (obj)
- drm_gem_object_unreference_unlocked(obj);
+ drm_gem_object_unreference_unlocked(obj);
return ERR_PTR(ret);
}
@@ -831,6 +1017,143 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev,
return _msm_gem_new(dev, size, flags, false);
}
+static struct drm_gem_object *msm_svm_gem_new_impl(struct drm_device *dev,
+ uint32_t size, uint32_t flags)
+{
+ struct msm_gem_svm_object *msm_svm_obj;
+ struct msm_gem_object *msm_obj;
+ int ret;
+
+ msm_svm_obj = kzalloc(sizeof(*msm_svm_obj), GFP_KERNEL);
+ if (!msm_svm_obj)
+ return ERR_PTR(-ENOMEM);
+
+ msm_obj = &msm_svm_obj->msm_obj_base;
+
+ ret = msm_gem_obj_init(dev, size, flags | MSM_BO_SVM, msm_obj, false);
+ if (ret) {
+ kfree(msm_svm_obj);
+ return ERR_PTR(ret);
+ }
+
+ return &msm_obj->base;
+}
+
+/* convenience method to construct an SVM GEM bo, and userspace handle */
+struct drm_gem_object *msm_gem_svm_new(struct drm_device *dev,
+ struct drm_file *file, uint64_t hostptr,
+ uint64_t size, uint32_t flags)
+{
+ struct drm_gem_object *obj;
+ struct msm_file_private *ctx = file->driver_priv;
+ struct msm_gem_address_space *aspace = ctx->aspace;
+ struct msm_gem_object *msm_obj;
+ struct msm_gem_svm_object *msm_svm_obj;
+ struct msm_gem_vma *domain = NULL;
+ struct page **p;
+ int npages;
+ int num_pinned = 0;
+ int write;
+ int ret;
+
+ /* if we don't have IOMMU, don't bother pretending we can import: */
+ if (!iommu_present(&platform_bus_type)) {
+ dev_err_once(dev->dev, "cannot import without IOMMU\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* hostptr and size must be page-aligned */
+ if (offset_in_page(hostptr | size))
+ return ERR_PTR(-EINVAL);
+
+ /* Only CPU cached SVM objects are allowed */
+ if ((flags & MSM_BO_CACHE_MASK) != MSM_BO_CACHED)
+ return ERR_PTR(-EINVAL);
+
+ /* Allocate and initialize a new msm_gem_object */
+ obj = msm_svm_gem_new_impl(dev, size, flags);
+ if (IS_ERR(obj))
+ return obj;
+
+ drm_gem_private_object_init(dev, obj, size);
+
+ msm_obj = to_msm_bo(obj);
+ domain = obj_add_domain(&msm_obj->base, aspace);
+ if (IS_ERR(domain)) {
+ drm_gem_object_unreference_unlocked(obj);
+ return ERR_CAST(domain);
+ }
+
+ /* Reserve iova if not already in use, else fail */
+ ret = msm_gem_reserve_iova(aspace, domain, hostptr, size);
+ if (ret) {
+ obj_remove_domain(domain);
+ drm_gem_object_unreference_unlocked(obj);
+ return ERR_PTR(ret);
+ }
+
+ msm_svm_obj = to_msm_svm_obj(msm_obj);
+ msm_svm_obj->hostptr = hostptr;
+ msm_svm_obj->invalid = false;
+
+ ret = msm_gem_mn_register(msm_svm_obj, aspace);
+ if (ret)
+ goto fail;
+
+ /*
+ * Get physical pages and map into smmu in the ioctl itself.
+ * The driver handles iova allocation, physical page allocation and
+ * SMMU map all in one go. If we break this, then we have to maintain
+ * state to tell if physical pages allocation/map needs to happen.
+ * For SVM, iova reservation needs to happen in the ioctl itself,
+ * so do the rest right here as well.
+ */
+ npages = size >> PAGE_SHIFT;
+ p = kcalloc(npages, sizeof(struct page *), GFP_KERNEL);
+ if (!p) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ write = (msm_obj->flags & MSM_BO_GPU_READONLY) ? 0 : 1;
+ /* This may hold mm->mmap_sem */
+ num_pinned = get_user_pages_fast(hostptr, npages, write, p);
+ if (num_pinned != npages) {
+ ret = -EINVAL;
+ goto free_pages;
+ }
+
+ msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
+ if (IS_ERR(msm_obj->sgt)) {
+ ret = PTR_ERR(msm_obj->sgt);
+ goto free_pages;
+ }
+
+ msm_obj->pages = p;
+
+ ret = aspace->mmu->funcs->map(aspace->mmu, domain->iova,
+ msm_obj->sgt, msm_obj->flags, get_dmabuf_ptr(obj));
+ if (ret)
+ goto free_pages;
+
+ kref_get(&aspace->kref);
+
+ return obj;
+
+free_pages:
+ release_pages(p, num_pinned, 0);
+ kfree(p);
+
+fail:
+ if (domain)
+ msm_gem_release_iova(aspace, domain);
+
+ obj_remove_domain(domain);
+ drm_gem_object_unreference_unlocked(obj);
+
+ return ERR_PTR(ret);
+}
+
struct drm_gem_object *msm_gem_import(struct drm_device *dev,
uint32_t size, struct sg_table *sgt, u32 flags)
{
@@ -846,10 +1169,9 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
size = PAGE_ALIGN(size);
- ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj, false);
-
- if (ret)
- goto fail;
+ obj = msm_gem_new_impl(dev, size, MSM_BO_WC, false);
+ if (IS_ERR(obj))
+ return obj;
drm_gem_private_object_init(dev, obj, size);
@@ -868,7 +1190,8 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
/* OR the passed in flags */
msm_obj->flags |= flags;
- ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, NULL, npages);
+ ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages,
+ NULL, npages);
if (ret) {
mutex_unlock(&msm_obj->lock);
goto fail;
@@ -879,8 +1202,86 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
return obj;
fail:
- if (obj)
- drm_gem_object_unreference_unlocked(obj);
+ drm_gem_object_unreference_unlocked(obj);
return ERR_PTR(ret);
}
+
+/* Timeout in ms, long enough so we are sure the GPU is hung */
+#define SVM_OBJ_WAIT_TIMEOUT 10000
+static void invalidate_svm_object(struct msm_gem_svm_object *msm_svm_obj)
+{
+ struct msm_gem_object *msm_obj = &msm_svm_obj->msm_obj_base;
+ struct drm_device *dev = msm_obj->base.dev;
+ struct msm_gem_vma *domain, *tmp;
+ uint32_t fence;
+ int ret;
+
+ if (is_active(msm_obj)) {
+ ktime_t timeout = ktime_add_ms(ktime_get(),
+ SVM_OBJ_WAIT_TIMEOUT);
+
+ /* Get the most recent fence that touches the object */
+ fence = msm_gem_fence(msm_obj, MSM_PREP_READ | MSM_PREP_WRITE);
+
+ /* Wait for the fence to retire */
+ ret = msm_wait_fence(dev, fence, &timeout, true);
+ if (ret)
+ /* The GPU could be hung! Not much we can do */
+ dev_err(dev->dev, "drm: Error (%d) waiting for svm object: 0x%llx",
+ ret, msm_svm_obj->hostptr);
+ }
+
+ /* GPU is done, unmap object from SMMU */
+ mutex_lock(&msm_obj->lock);
+ list_for_each_entry_safe(domain, tmp, &msm_obj->domains, list) {
+ struct msm_gem_address_space *aspace = domain->aspace;
+
+ if (domain->iova)
+ aspace->mmu->funcs->unmap(aspace->mmu,
+ domain->iova, msm_obj->sgt,
+ get_dmabuf_ptr(&msm_obj->base));
+ }
+ /* Let go of the physical pages */
+ put_pages(&msm_obj->base);
+ mutex_unlock(&msm_obj->lock);
+}
+
+void msm_mn_invalidate_range_start(struct mmu_notifier *mn,
+ struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+ struct msm_mmu_notifier *msm_mn =
+ container_of(mn, struct msm_mmu_notifier, mn);
+ struct interval_tree_node *itn = NULL;
+ struct msm_gem_svm_object *msm_svm_obj;
+ struct drm_gem_object *obj;
+ LIST_HEAD(inv_list);
+
+ if (!msm_gem_mn_get(msm_mn))
+ return;
+
+ spin_lock(&msm_mn->svm_tree_lock);
+ itn = interval_tree_iter_first(&msm_mn->svm_tree, start, end - 1);
+ while (itn) {
+ msm_svm_obj = container_of(itn,
+ struct msm_gem_svm_object, svm_node);
+ obj = &msm_svm_obj->msm_obj_base.base;
+
+ if (kref_get_unless_zero(&obj->refcount))
+ list_add(&msm_svm_obj->lnode, &inv_list);
+
+ itn = interval_tree_iter_next(itn, start, end - 1);
+ }
+ spin_unlock(&msm_mn->svm_tree_lock);
+
+ list_for_each_entry(msm_svm_obj, &inv_list, lnode) {
+ obj = &msm_svm_obj->msm_obj_base.base;
+ /* Unregister SVM object from mmu notifications */
+ msm_gem_mn_unregister(msm_svm_obj);
+ msm_svm_obj->invalid = true;
+ invalidate_svm_object(msm_svm_obj);
+ drm_gem_object_unreference_unlocked(obj);
+ }
+
+ msm_gem_mn_put(msm_mn);
+}
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index eb850952f1f5..e8528892939f 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -20,11 +20,14 @@
#include <linux/kref.h>
#include <linux/reservation.h>
+#include <linux/mmu_notifier.h>
+#include <linux/interval_tree.h>
#include "msm_drv.h"
/* Additional internal-use only BO flags: */
#define MSM_BO_STOLEN 0x10000000 /* try to use stolen/splash memory */
#define MSM_BO_LOCKED 0x20000000 /* Pages have been securely locked */
+#define MSM_BO_SVM 0x40000000 /* bo is SVM */
struct msm_gem_address_space {
const char *name;
@@ -85,6 +88,32 @@ struct msm_gem_object {
};
#define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
+struct msm_mmu_notifier {
+ struct mmu_notifier mn;
+ struct mm_struct *mm; /* mm_struct owning the mmu notifier mn */
+ struct hlist_node node;
+ struct rb_root svm_tree; /* interval tree holding all svm bos */
+ spinlock_t svm_tree_lock; /* Protects svm_tree*/
+ struct msm_drm_private *msm_dev;
+ struct kref refcount;
+};
+
+struct msm_gem_svm_object {
+ struct msm_gem_object msm_obj_base;
+ uint64_t hostptr;
+ struct mm_struct *mm; /* mm_struct the svm bo belongs to */
+ struct interval_tree_node svm_node;
+ struct msm_mmu_notifier *msm_mn;
+ struct list_head lnode;
+ /* bo has been unmapped on CPU, cannot be part of GPU submits */
+ bool invalid;
+};
+
+#define to_msm_svm_obj(x) \
+ ((struct msm_gem_svm_object *) \
+ container_of(x, struct msm_gem_svm_object, msm_obj_base))
+
+
static inline bool is_active(struct msm_gem_object *msm_obj)
{
return msm_obj->gpu != NULL;
@@ -103,6 +132,9 @@ static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj,
return fence;
}
+/* Internal submit flags */
+#define SUBMIT_FLAG_SKIP_HANGCHECK 0x00000001
+
/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
* associated with the cmdstream submission for synchronization (and
* make it easier to unwind when things go wrong, etc). This only
@@ -111,15 +143,17 @@ static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj,
struct msm_gem_submit {
struct drm_device *dev;
struct msm_gem_address_space *aspace;
- struct list_head node; /* node in gpu submit_list */
+ struct list_head node; /* node in ring submit list */
struct list_head bo_list;
struct ww_acquire_ctx ticket;
uint32_t fence;
int ring;
+ u32 flags;
bool valid;
uint64_t profile_buf_iova;
void *profile_buf_vaddr;
bool secure;
+ struct msm_gpu_submitqueue *queue;
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 c9b3151180dc..b9ace0d9e69b 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -35,7 +35,8 @@ static inline void __user *to_user_ptr(u64 address)
static struct msm_gem_submit *submit_create(struct drm_device *dev,
struct msm_gem_address_space *aspace,
- uint32_t nr_bos, uint32_t nr_cmds)
+ uint32_t nr_bos, uint32_t nr_cmds,
+ struct msm_gpu_submitqueue *queue)
{
struct msm_gem_submit *submit;
uint64_t sz = sizeof(*submit) + (nr_bos * sizeof(submit->bos[0])) +
@@ -48,6 +49,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev,
if (submit) {
submit->dev = dev;
submit->aspace = aspace;
+ submit->queue = queue;
/* initially, until copy_from_user() and bo lookup succeeds: */
submit->nr_bos = 0;
@@ -59,6 +61,11 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev,
submit->secure = false;
+ /*
+ * Initalize node so we can safely list_del() on it if
+ * we fail in the submit path
+ */
+ INIT_LIST_HEAD(&submit->node);
INIT_LIST_HEAD(&submit->bo_list);
ww_acquire_init(&submit->ticket, &reservation_ww_class);
}
@@ -74,6 +81,16 @@ copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
return -EFAULT;
}
+void msm_gem_submit_free(struct msm_gem_submit *submit)
+{
+ if (!submit)
+ return;
+
+ msm_submitqueue_put(submit->queue);
+ list_del(&submit->node);
+ kfree(submit);
+}
+
static int submit_lookup_objects(struct msm_gpu *gpu,
struct msm_gem_submit *submit,
struct drm_msm_gem_submit *args, struct drm_file *file)
@@ -217,6 +234,18 @@ retry:
submit->bos[i].flags |= BO_LOCKED;
}
+ /*
+ * An invalid SVM object is part of
+ * this submit's buffer list, fail.
+ */
+ if (msm_obj->flags & MSM_BO_SVM) {
+ struct msm_gem_svm_object *msm_svm_obj =
+ to_msm_svm_obj(msm_obj);
+ if (msm_svm_obj->invalid) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
/* if locking succeeded, pin bo: */
ret = msm_gem_get_iova(&msm_obj->base, aspace, &iova);
@@ -369,6 +398,9 @@ static void submit_cleanup(struct msm_gpu *gpu, struct msm_gem_submit *submit,
{
unsigned i;
+ if (!submit)
+ return;
+
for (i = 0; i < submit->nr_bos; i++) {
struct msm_gem_object *msm_obj = submit->bos[i].obj;
submit_unlock_unpin_bo(gpu, submit, i);
@@ -386,6 +418,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
struct drm_msm_gem_submit *args = data;
struct msm_file_private *ctx = file->driver_priv;
struct msm_gem_submit *submit;
+ struct msm_gpu_submitqueue *queue;
struct msm_gpu *gpu;
unsigned i;
int ret;
@@ -400,9 +433,14 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
if (!gpu)
return -ENXIO;
+ queue = msm_submitqueue_get(ctx, args->queueid);
+ if (!queue)
+ return -ENOENT;
+
mutex_lock(&dev->struct_mutex);
- submit = submit_create(dev, ctx->aspace, args->nr_bos, args->nr_cmds);
+ submit = submit_create(dev, ctx->aspace, args->nr_bos, args->nr_cmds,
+ queue);
if (!submit) {
ret = -ENOMEM;
goto out;
@@ -422,6 +460,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
struct msm_gem_object *msm_obj;
uint64_t iova;
+ size_t size;
ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
if (ret) {
@@ -454,10 +493,12 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
goto out;
}
- if (!(submit_cmd.size) ||
- ((submit_cmd.size + submit_cmd.submit_offset) >
- msm_obj->base.size)) {
- DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
+ size = submit_cmd.size + submit_cmd.submit_offset;
+
+ if (!submit_cmd.size || (size < submit_cmd.size) ||
+ (size > msm_obj->base.size)) {
+ DRM_ERROR("invalid cmdstream offset/size: %u/%u\n",
+ submit_cmd.submit_offset, submit_cmd.size);
ret = -EINVAL;
goto out;
}
@@ -470,7 +511,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
if (submit_cmd.type == MSM_SUBMIT_CMD_PROFILE_BUF) {
submit->profile_buf_iova = submit->cmd[i].iova;
submit->profile_buf_vaddr =
- msm_gem_vaddr(&msm_obj->base);
+ msm_gem_vaddr(&msm_obj->base) +
+ submit_cmd.submit_offset;
}
if (submit->valid)
@@ -485,17 +527,16 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
submit->nr_cmds = i;
/* Clamp the user submitted ring to the range of available rings */
- submit->ring = clamp_t(uint32_t,
- (args->flags & MSM_SUBMIT_RING_MASK) >> MSM_SUBMIT_RING_SHIFT,
- 0, gpu->nr_rings - 1);
+ submit->ring = clamp_t(uint32_t, queue->prio, 0, gpu->nr_rings - 1);
ret = msm_gpu_submit(gpu, submit);
args->fence = submit->fence;
out:
- if (submit)
- submit_cleanup(gpu, submit, !!ret);
+ submit_cleanup(gpu, submit, !!ret);
+ if (ret)
+ msm_gem_submit_free(submit);
mutex_unlock(&dev->struct_mutex);
return ret;
}
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 12e3c0f7c101..f399d24019e4 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -88,7 +88,7 @@ static int allocate_iova(struct msm_gem_address_space *aspace,
return 0;
}
ret = drm_mm_insert_node(&aspace->mm, &vma->node,
- size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_DEFAULT);
+ size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_BOTTOM_UP);
spin_unlock(&aspace->lock);
@@ -98,6 +98,45 @@ static int allocate_iova(struct msm_gem_address_space *aspace,
return ret;
}
+int msm_gem_reserve_iova(struct msm_gem_address_space *aspace,
+ struct msm_gem_vma *vma,
+ uint64_t hostptr, uint64_t size)
+{
+ struct drm_mm *mm = &aspace->mm;
+ uint64_t start = hostptr >> PAGE_SHIFT;
+ uint64_t last = (hostptr + size - 1) >> PAGE_SHIFT;
+ int ret;
+
+ spin_lock(&aspace->lock);
+
+ if (drm_mm_interval_first(mm, start, last)) {
+ /* iova already in use, fail */
+ spin_unlock(&aspace->lock);
+ return -EADDRINUSE;
+ }
+
+ vma->node.start = hostptr >> PAGE_SHIFT;
+ vma->node.size = size >> PAGE_SHIFT;
+ vma->node.color = 0;
+
+ ret = drm_mm_reserve_node(mm, &vma->node);
+ if (!ret)
+ vma->iova = hostptr;
+
+ spin_unlock(&aspace->lock);
+
+ return ret;
+}
+
+void msm_gem_release_iova(struct msm_gem_address_space *aspace,
+ struct msm_gem_vma *vma)
+{
+ spin_lock(&aspace->lock);
+ if (drm_mm_node_allocated(&vma->node))
+ drm_mm_remove_node(&vma->node);
+ spin_unlock(&aspace->lock);
+}
+
int msm_gem_map_vma(struct msm_gem_address_space *aspace,
struct msm_gem_vma *vma, struct sg_table *sgt,
void *priv, unsigned int flags)
@@ -116,11 +155,7 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace,
flags, priv);
if (ret) {
- spin_lock(&aspace->lock);
- if (drm_mm_node_allocated(&vma->node))
- drm_mm_remove_node(&vma->node);
- spin_unlock(&aspace->lock);
-
+ msm_gem_release_iova(aspace, vma);
return ret;
}
@@ -131,17 +166,16 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace,
}
void msm_gem_unmap_vma(struct msm_gem_address_space *aspace,
- struct msm_gem_vma *vma, struct sg_table *sgt, void *priv)
+ struct msm_gem_vma *vma, struct sg_table *sgt,
+ void *priv, bool invalidated)
{
if (!aspace || !vma->iova)
return;
- aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv);
+ if (!invalidated)
+ aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv);
- spin_lock(&aspace->lock);
- if (drm_mm_node_allocated(&vma->node))
- drm_mm_remove_node(&vma->node);
- spin_unlock(&aspace->lock);
+ msm_gem_release_iova(aspace, vma);
vma->iova = 0;
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 81bab9cc22af..45d7a658f022 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -278,7 +278,8 @@ static void inactive_start(struct msm_gpu *gpu)
* Hangcheck detection for locked gpu:
*/
-static void retire_submits(struct msm_gpu *gpu, uint32_t fence);
+static void retire_submits(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
+ uint32_t fence);
static void recover_worker(struct work_struct *work)
{
@@ -296,29 +297,22 @@ static void recover_worker(struct work_struct *work)
inactive_cancel(gpu);
FOR_EACH_RING(gpu, ring, i) {
- uint32_t fence;
+ uint32_t fence = gpu->funcs->last_fence(gpu, ring);
- if (!ring)
- continue;
-
- fence = gpu->funcs->last_fence(gpu, ring);
-
- /*
- * Retire the faulting command on the active ring and
- * make sure the other rings are cleaned up
- */
- if (ring == gpu->funcs->active_ring(gpu))
- retire_submits(gpu, fence + 1);
- else
- retire_submits(gpu, fence);
+ retire_submits(gpu, ring,
+ gpu->funcs->active_ring(gpu) == ring ?
+ fence + 1 : fence);
}
/* Recover the GPU */
gpu->funcs->recover(gpu);
- /* replay the remaining submits for all rings: */
- list_for_each_entry(submit, &gpu->submit_list, node) {
- gpu->funcs->submit(gpu, submit);
+ /* Replay the remaining on all rings, highest priority first */
+ for (i = gpu->nr_rings - 1; i >= 0; i--) {
+ struct msm_ringbuffer *ring = gpu->rb[i];
+
+ list_for_each_entry(submit, &ring->submits, node)
+ gpu->funcs->submit(gpu, submit);
}
}
mutex_unlock(&dev->struct_mutex);
@@ -465,23 +459,19 @@ out:
* Cmdstream submission/retirement:
*/
-static void retire_submits(struct msm_gpu *gpu, uint32_t fence)
+static void retire_submits(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
+ uint32_t fence)
{
struct drm_device *dev = gpu->dev;
struct msm_gem_submit *submit, *tmp;
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
- /*
- * Find and retire all the submits in the same ring that are older than
- * or equal to 'fence'
- */
+ list_for_each_entry_safe(submit, tmp, &ring->submits, node) {
+ if (submit->fence > fence)
+ break;
- list_for_each_entry_safe(submit, tmp, &gpu->submit_list, node) {
- if (COMPARE_FENCE_LTE(submit->fence, fence)) {
- list_del(&submit->node);
- kfree(submit);
- }
+ msm_gem_submit_free(submit);
}
}
@@ -493,11 +483,12 @@ static bool _fence_signaled(struct msm_gem_object *obj, uint32_t fence)
return COMPARE_FENCE_LTE(obj->read_fence, fence);
}
-static void _retire_ring(struct msm_gpu *gpu, uint32_t fence)
+static void _retire_ring(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
+ uint32_t fence)
{
struct msm_gem_object *obj, *tmp;
- retire_submits(gpu, fence);
+ retire_submits(gpu, ring, fence);
list_for_each_entry_safe(obj, tmp, &gpu->active_list, mm_list) {
if (_fence_signaled(obj, fence)) {
@@ -525,7 +516,7 @@ static void retire_worker(struct work_struct *work)
msm_update_fence(gpu->dev, fence);
mutex_lock(&dev->struct_mutex);
- _retire_ring(gpu, fence);
+ _retire_ring(gpu, ring, fence);
mutex_unlock(&dev->struct_mutex);
}
@@ -555,7 +546,7 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
inactive_cancel(gpu);
- list_add_tail(&submit->node, &gpu->submit_list);
+ list_add_tail(&submit->node, &ring->submits);
msm_rd_dump_submit(submit);
@@ -818,7 +809,6 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
INIT_WORK(&gpu->inactive_work, inactive_worker);
INIT_WORK(&gpu->recover_work, recover_worker);
- INIT_LIST_HEAD(&gpu->submit_list);
setup_timer(&gpu->inactive_timer, inactive_handler,
(unsigned long)gpu);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index a47eae68dd9b..cddbcbbb8557 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -147,12 +147,18 @@ struct msm_gpu {
struct timer_list hangcheck_timer;
uint32_t hangcheck_fence[MSM_GPU_MAX_RINGS];
struct work_struct recover_work;
-
- struct list_head submit_list;
-
struct msm_snapshot *snapshot;
};
+struct msm_gpu_submitqueue {
+ int id;
+ u32 flags;
+ u32 prio;
+ int faults;
+ struct list_head node;
+ struct kref ref;
+};
+
/* It turns out that all targets use the same ringbuffer size. */
#define MSM_GPU_RINGBUFFER_SZ SZ_32K
#define MSM_GPU_RINGBUFFER_BLKSIZE 32
@@ -280,4 +286,10 @@ void msm_gpu_cleanup_counters(struct msm_gpu *gpu,
u64 msm_gpu_counter_read(struct msm_gpu *gpu,
struct drm_msm_counter_read *data);
+static inline void msm_submitqueue_put(struct msm_gpu_submitqueue *queue)
+{
+ if (queue)
+ kref_put(&queue->ref, msm_submitqueue_destroy);
+}
+
#endif /* __MSM_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h
index 033370ccbe24..8148d3e9e850 100644
--- a/drivers/gpu/drm/msm/msm_mmu.h
+++ b/drivers/gpu/drm/msm/msm_mmu.h
@@ -83,4 +83,8 @@ static inline void msm_mmu_disable(struct msm_mmu *mmu)
mmu->funcs->disable(mmu);
}
+/* SDE smmu driver initialize and cleanup functions */
+int __init msm_smmu_driver_init(void);
+void __exit msm_smmu_driver_cleanup(void);
+
#endif /* __MSM_MMU_H__ */
diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c
index 2d112f24a902..edf3ff2a7a61 100644
--- a/drivers/gpu/drm/msm/msm_rd.c
+++ b/drivers/gpu/drm/msm/msm_rd.c
@@ -27,6 +27,11 @@
* This bypasses drm_debugfs_create_files() mainly because we need to use
* our own fops for a bit more control. In particular, we don't want to
* do anything if userspace doesn't have the debugfs file open.
+ *
+ * The module-param "rd_full", which defaults to false, enables snapshotting
+ * all (non-written) buffers in the submit, rather than just cmdstream bo's.
+ * This is useful to capture the contents of (for example) vbo's or textures,
+ * or shader programs (if not emitted inline in cmdstream).
*/
#ifdef CONFIG_DEBUG_FS
@@ -40,6 +45,10 @@
#include "msm_gpu.h"
#include "msm_gem.h"
+static bool rd_full = false;
+MODULE_PARM_DESC(rd_full, "If true, $debugfs/.../rd will snapshot all buffer contents");
+module_param_named(rd_full, rd_full, bool, 0600);
+
enum rd_sect_type {
RD_NONE,
RD_TEST, /* ascii text */
@@ -277,6 +286,36 @@ void msm_rd_debugfs_cleanup(struct drm_minor *minor)
kfree(rd);
}
+static void snapshot_buf(struct msm_rd_state *rd,
+ struct msm_gem_submit *submit, int idx,
+ uint64_t iova, uint32_t size)
+{
+ struct msm_gem_object *obj = submit->bos[idx].obj;
+ uint64_t offset = 0;
+
+ if (iova) {
+ offset = iova - submit->bos[idx].iova;
+ } else {
+ iova = submit->bos[idx].iova;
+ size = obj->base.size;
+ }
+
+ /* Always write the RD_GPUADDR so we know how big the buffer is */
+ rd_write_section(rd, RD_GPUADDR,
+ (uint64_t[2]) { iova, size }, 16);
+
+ /* But only dump contents for buffers marked as read and not secure */
+ if (submit->bos[idx].flags & MSM_SUBMIT_BO_READ &&
+ !(obj->flags & MSM_BO_SECURE)) {
+ const char *buf = msm_gem_vaddr(&obj->base);
+
+ if (IS_ERR_OR_NULL(buf))
+ return;
+
+ rd_write_section(rd, RD_BUFFER_CONTENTS, buf + offset, size);
+ }
+}
+
/* called under struct_mutex */
void msm_rd_dump_submit(struct msm_gem_submit *submit)
{
@@ -300,24 +339,20 @@ void msm_rd_dump_submit(struct msm_gem_submit *submit)
rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
- /* could be nice to have an option (module-param?) to snapshot
- * all the bo's associated with the submit. Handy to see vtx
- * buffers, etc. For now just the cmdstream bo's is enough.
- */
+ if (rd_full) {
+ for (i = 0; i < submit->nr_bos; i++)
+ snapshot_buf(rd, submit, i, 0, 0);
+ }
for (i = 0; i < submit->nr_cmds; i++) {
- uint32_t idx = submit->cmd[i].idx;
uint64_t iova = submit->cmd[i].iova;
uint32_t szd = submit->cmd[i].size; /* in dwords */
- struct msm_gem_object *obj = submit->bos[idx].obj;
- const char *buf = msm_gem_vaddr(&obj->base);
- buf += iova - submit->bos[idx].iova;
-
- rd_write_section(rd, RD_GPUADDR,
- (uint64_t[2]) { iova, szd * 4 }, 16);
- rd_write_section(rd, RD_BUFFER_CONTENTS,
- buf, szd * 4);
+ /* snapshot cmdstream bo's (if we haven't already): */
+ if (!rd_full) {
+ snapshot_buf(rd, submit, submit->cmd[i].idx,
+ submit->cmd[i].iova, szd * 4);
+ }
switch (submit->cmd[i].type) {
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c
index 382c71bb0ebe..458f80426b39 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -47,6 +47,7 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id)
ring->next = ring->start;
ring->cur = ring->start;
+ INIT_LIST_HEAD(&ring->submits);
spin_lock_init(&ring->lock);
return ring;
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h
index 1e84905073bf..70643a7b4cc4 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.h
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.h
@@ -28,6 +28,7 @@ struct msm_ringbuffer {
uint64_t iova;
uint32_t submitted_fence;
spinlock_t lock;
+ struct list_head submits;
};
struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id);
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index a6efb22b5ed4..7673f54b7691 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -133,7 +133,7 @@ static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova,
domain = client->mmu_mapping->domain;
return iommu_map_sg(domain, iova, sgt->sgl,
- sgt->nents, flags);
+ sgt->nents, flags);
} else {
if (priv)
ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl,
@@ -151,27 +151,13 @@ static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova,
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
- struct iommu_domain *domain = client->mmu_mapping->domain;
- struct scatterlist *sg;
- size_t len = 0;
- int unmapped, i = 0;
- if (iova != 0) {
- for_each_sg(sgt->sgl, sg, sgt->nents, i)
- len += sg->length;
-
- unmapped = iommu_unmap(domain, iova, len);
- if (unmapped < len)
- dev_warn(mmu->dev,
- "could not unmap iova@%llx\n", iova);
- } else {
- if (priv)
- msm_dma_unmap_sg(client->dev, sgt->sgl,
- sgt->nents, DMA_BIDIRECTIONAL, priv);
- else
- dma_unmap_sg(client->dev, sgt->sgl, sgt->nents,
- DMA_BIDIRECTIONAL);
- }
+ if (priv)
+ msm_dma_unmap_sg(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL, priv);
+ else
+ dma_unmap_sg(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL);
}
static void msm_smmu_destroy(struct msm_mmu *mmu)
@@ -220,13 +206,13 @@ static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = {
};
static const struct of_device_id msm_smmu_dt_match[] = {
- { .compatible = "qcom,smmu_mdp_unsec",
+ { .compatible = "qcom,smmu_sde_unsec",
.data = &msm_smmu_domains[MSM_SMMU_DOMAIN_UNSECURE] },
- { .compatible = "qcom,smmu_mdp_sec",
+ { .compatible = "qcom,smmu_sde_sec",
.data = &msm_smmu_domains[MSM_SMMU_DOMAIN_SECURE] },
- { .compatible = "qcom,smmu_rot_unsec",
+ { .compatible = "qcom,smmu_sde_nrt_unsec",
.data = &msm_smmu_domains[MSM_SMMU_DOMAIN_NRT_UNSECURE] },
- { .compatible = "qcom,smmu_rot_sec",
+ { .compatible = "qcom,smmu_sde_nrt_sec",
.data = &msm_smmu_domains[MSM_SMMU_DOMAIN_NRT_SECURE] },
{}
};
@@ -422,7 +408,7 @@ static struct platform_driver msm_smmu_driver = {
},
};
-static int __init msm_smmu_driver_init(void)
+int __init msm_smmu_driver_init(void)
{
int ret;
@@ -432,13 +418,11 @@ static int __init msm_smmu_driver_init(void)
return ret;
}
-module_init(msm_smmu_driver_init);
-static void __exit msm_smmu_driver_cleanup(void)
+void __exit msm_smmu_driver_cleanup(void)
{
platform_driver_unregister(&msm_smmu_driver);
}
-module_exit(msm_smmu_driver_cleanup);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MSM SMMU driver");
diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c
new file mode 100644
index 000000000000..8e29021bb0d8
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_submitqueue.c
@@ -0,0 +1,125 @@
+/* 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/kref.h>
+#include "msm_gpu.h"
+
+void msm_submitqueue_destroy(struct kref *kref)
+{
+ struct msm_gpu_submitqueue *queue = container_of(kref,
+ struct msm_gpu_submitqueue, ref);
+
+ kfree(queue);
+}
+
+struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
+ u32 id)
+{
+ struct msm_gpu_submitqueue *entry;
+
+ read_lock(&ctx->queuelock);
+
+ list_for_each_entry(entry, &ctx->submitqueues, node) {
+ if (entry->id == id) {
+ kref_get(&entry->ref);
+ read_unlock(&ctx->queuelock);
+
+ return entry;
+ }
+ }
+
+ read_unlock(&ctx->queuelock);
+ return NULL;
+}
+
+void msm_submitqueue_close(struct msm_file_private *ctx)
+{
+ struct msm_gpu_submitqueue *entry, *tmp;
+
+ /*
+ * No lock needed in close and there won't
+ * be any more user ioctls coming our way
+ */
+
+ list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node)
+ msm_submitqueue_put(entry);
+}
+
+int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, u32 flags,
+ u32 *id)
+{
+ struct msm_gpu_submitqueue *queue = kzalloc(sizeof(*queue), GFP_KERNEL);
+
+ if (!queue)
+ return -ENOMEM;
+
+ kref_init(&queue->ref);
+ queue->flags = flags;
+ queue->prio = prio;
+
+ write_lock(&ctx->queuelock);
+
+ queue->id = ctx->queueid++;
+
+ if (id)
+ *id = queue->id;
+
+ list_add_tail(&queue->node, &ctx->submitqueues);
+
+ write_unlock(&ctx->queuelock);
+
+ return 0;
+}
+
+int msm_submitqueue_init(struct msm_file_private *ctx)
+{
+ INIT_LIST_HEAD(&ctx->submitqueues);
+
+ rwlock_init(&ctx->queuelock);
+
+ /*
+ * Add the "default" submitqueue with id 0
+ * "medium" priority (3) and no flags
+ */
+
+ return msm_submitqueue_create(ctx, 3, 0, NULL);
+}
+
+int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id)
+{
+ struct msm_gpu_submitqueue *entry;
+
+ /*
+ * id 0 is the "default" queue and can't be destroyed
+ * by the user
+ */
+
+ if (!id)
+ return -ENOENT;
+
+ write_lock(&ctx->queuelock);
+
+ list_for_each_entry(entry, &ctx->submitqueues, node) {
+ if (entry->id == id) {
+ list_del(&entry->node);
+ write_unlock(&ctx->queuelock);
+
+ msm_submitqueue_put(entry);
+ return 0;
+ }
+ }
+
+ write_unlock(&ctx->queuelock);
+ return -ENOENT;
+}
+
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index adfe9e54e6f4..cb5f7d3cf19f 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -57,6 +57,7 @@
static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
{
struct msm_drm_private *priv = crtc->dev->dev_private;
+
return to_sde_kms(priv->kms);
}
@@ -183,9 +184,6 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
pstate = to_sde_plane_state(plane->state);
- flush_mask = ctl->ops.get_bitmask_sspp(ctl,
- sde_plane_pipe(plane));
-
/* always stage plane on either left or right lm */
if (plane->state->crtc_x >= crtc_split_width) {
lm_idx = RIGHT_MIXER;
@@ -195,20 +193,36 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
idx = left_crtc_zpos_cnt[pstate->stage]++;
}
+ /*
+ * program each mixer with two hw pipes in dual mixer mode,
+ */
+ if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) {
+ stage_cfg->stage[LEFT_MIXER][pstate->stage][1] =
+ sde_plane_pipe(plane, 1);
+
+ flush_mask = ctl->ops.get_bitmask_sspp(ctl,
+ sde_plane_pipe(plane, 1));
+ }
+
+ flush_mask |= ctl->ops.get_bitmask_sspp(ctl,
+ sde_plane_pipe(plane, lm_idx ? 1 : 0));
+
/* stage plane on right LM if it crosses the boundary */
lm_right = (lm_idx == LEFT_MIXER) &&
(plane->state->crtc_x + plane->state->crtc_w >
crtc_split_width);
stage_cfg->stage[lm_idx][pstate->stage][idx] =
- sde_plane_pipe(plane);
+ sde_plane_pipe(plane, lm_idx ? 1 : 0);
+
mixer[lm_idx].flush_mask |= flush_mask;
SDE_DEBUG("crtc %d stage:%d - plane %d sspp %d fb %d\n",
crtc->base.id,
pstate->stage,
plane->base.id,
- sde_plane_pipe(plane) - SSPP_VIG0,
+ sde_plane_pipe(plane,
+ lm_idx ? 1 : 0) - SSPP_VIG0,
plane->state->fb ?
plane->state->fb->base.id : -1);
@@ -230,8 +244,19 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
if (lm_right) {
idx = right_crtc_zpos_cnt[pstate->stage]++;
- stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] =
- sde_plane_pipe(plane);
+
+ /*
+ * program each mixer with two hw pipes
+ in dual mixer mode,
+ */
+ if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) {
+ stage_cfg->stage[RIGHT_MIXER][pstate->stage][1]
+ = sde_plane_pipe(plane, 0);
+ }
+
+ stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx]
+ = sde_plane_pipe(plane, 1);
+
mixer[RIGHT_MIXER].flush_mask |= flush_mask;
/* blend config update */
@@ -1256,7 +1281,8 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
return 0;
}
-void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file)
+void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc,
+ struct drm_file *file)
{
struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
@@ -1275,6 +1301,10 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
struct drm_device *dev;
struct sde_kms_info *info;
struct sde_kms *sde_kms;
+ static const struct drm_prop_enum_list e_secure_level[] = {
+ {SDE_DRM_SEC_NON_SEC, "sec_and_non_sec"},
+ {SDE_DRM_SEC_ONLY, "sec_only"},
+ };
SDE_DEBUG("\n");
@@ -1320,6 +1350,12 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
msm_property_install_blob(&sde_crtc->property_info, "capabilities",
DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
+
+ msm_property_install_enum(&sde_crtc->property_info, "security_level",
+ 0x0, 0, e_secure_level,
+ ARRAY_SIZE(e_secure_level),
+ CRTC_PROP_SECURITY_LEVEL);
+
sde_kms_info_reset(info);
sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion);
@@ -1665,7 +1701,8 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
#endif
/* initialize crtc */
-struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
+struct drm_crtc *sde_crtc_init(struct drm_device *dev,
+ struct drm_plane *plane)
{
struct drm_crtc *crtc = NULL;
struct sde_crtc *sde_crtc = NULL;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 519288f0dda2..17b678cfca46 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -14,6 +14,8 @@
#include <linux/slab.h>
#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
#include "sde_hw_mdss.h"
#include "sde_hw_catalog.h"
#include "sde_hw_catalog_format.h"
@@ -715,6 +717,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
sblk->pcc_blk.len = 0;
set_bit(SDE_SSPP_PCC, &sspp->features);
}
+ snprintf(sspp->name, sizeof(sspp->name), "vig%d", *vig_count-1);
}
static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
@@ -753,6 +756,7 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
sblk->pcc_blk.len = 0;
set_bit(SDE_SSPP_PCC, &sspp->features);
}
+ snprintf(sspp->name, sizeof(sspp->name), "rgb%d", *rgb_count-1);
}
static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg,
@@ -766,6 +770,7 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg,
sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count;
sblk->format_list = plane_formats;
(*cursor_count)++;
+ snprintf(sspp->name, sizeof(sspp->name), "cursor%d", *cursor_count-1);
}
static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg,
@@ -779,6 +784,7 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg,
sblk->format_list = plane_formats;
set_bit(SDE_SSPP_QOS, &sspp->features);
(*dma_count)++;
+ snprintf(sspp->name, sizeof(sspp->name), "dma%d", *dma_count-1);
}
static int sde_sspp_parse_dt(struct device_node *np,
@@ -1200,7 +1206,8 @@ end:
return rc;
}
-static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
+static int sde_wb_parse_dt(struct device_node *np,
+ struct sde_mdss_cfg *sde_cfg)
{
int rc, prop_count[WB_PROP_MAX], i, j;
struct sde_prop_value *prop_value = NULL;
@@ -1686,7 +1693,8 @@ end:
return rc;
}
-static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
+static int sde_pp_parse_dt(struct device_node *np,
+ struct sde_mdss_cfg *sde_cfg)
{
int rc, prop_count[PP_PROP_MAX], i;
struct sde_prop_value *prop_value = NULL;
@@ -1760,6 +1768,94 @@ end:
return rc;
}
+static inline u32 _sde_parse_sspp_id(struct sde_mdss_cfg *cfg,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; i < cfg->sspp_count; i++) {
+ if (!strcmp(cfg->sspp[i].name, name))
+ return cfg->sspp[i].id;
+ }
+
+ return SSPP_NONE;
+}
+
+static int _sde_vp_parse_dt(struct device_node *np,
+ struct sde_mdss_cfg *cfg)
+{
+ int rc = 0, i = 0;
+ struct device_node *node = NULL;
+ struct device_node *root_node = NULL;
+ struct sde_vp_cfg *vp;
+ struct sde_vp_sub_blks *vp_sub, *vp_sub_next;
+ struct property *prop;
+ const char *cname;
+
+ root_node = of_get_child_by_name(np, "qcom,sde-plane-id-map");
+ if (!root_node) {
+ root_node = of_parse_phandle(np, "qcom,sde-plane-id-map", 0);
+ if (!root_node) {
+ SDE_ERROR("No entry present for qcom,sde-plane-id-map");
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+
+ for_each_child_of_node(root_node, node) {
+ if (i >= MAX_BLOCKS) {
+ SDE_ERROR("num of nodes(%d) is bigger than max(%d)\n",
+ i, MAX_BLOCKS);
+ rc = -EINVAL;
+ goto end;
+ }
+ cfg->vp_count++;
+ vp = &(cfg->vp[i]);
+ vp->id = i;
+ rc = of_property_read_string(node, "qcom,display-type",
+ &(vp->display_type));
+ if (rc) {
+ SDE_ERROR("failed to read display-type, rc = %d\n", rc);
+ goto end;
+ }
+
+ rc = of_property_read_string(node, "qcom,plane-type",
+ &(vp->plane_type));
+ if (rc) {
+ SDE_ERROR("failed to read plane-type, rc = %d\n", rc);
+ goto end;
+ }
+
+ INIT_LIST_HEAD(&vp->sub_blks);
+ of_property_for_each_string(node, "qcom,plane-name",
+ prop, cname) {
+ vp_sub = kzalloc(sizeof(*vp_sub), GFP_KERNEL);
+ if (!vp_sub) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ vp_sub->sspp_id = _sde_parse_sspp_id(cfg, cname);
+ list_add_tail(&vp_sub->pipeid_list, &vp->sub_blks);
+ }
+ i++;
+ }
+
+end:
+ if (rc && cfg->vp_count) {
+ vp = &(cfg->vp[i]);
+ for (i = 0; i < cfg->vp_count; i++) {
+ list_for_each_entry_safe(vp_sub, vp_sub_next,
+ &vp->sub_blks, pipeid_list) {
+ list_del(&vp_sub->pipeid_list);
+ kfree(vp_sub);
+ }
+ }
+ memset(&(cfg->vp[0]), 0, sizeof(cfg->vp));
+ cfg->vp_count = 0;
+ }
+ return rc;
+}
+
static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
{
int rc, len, prop_count[SDE_PROP_MAX];
@@ -1851,7 +1947,8 @@ end:
return rc;
}
-static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
+static int sde_perf_parse_dt(struct device_node *np,
+ struct sde_mdss_cfg *cfg)
{
int rc, len, prop_count[PERF_PROP_MAX];
struct sde_prop_value *prop_value = NULL;
@@ -1891,7 +1988,8 @@ end:
return rc;
}
-static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
+static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg,
+ uint32_t hw_rev)
{
switch (hw_rev) {
case SDE_HW_VER_170:
@@ -1909,6 +2007,7 @@ static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg)
{
int i;
+ struct sde_vp_sub_blks *vp_sub, *vp_sub_next;
if (!sde_cfg)
return;
@@ -1932,13 +2031,23 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg)
kfree(sde_cfg->vbif[i].dynamic_ot_rd_tbl.cfg);
kfree(sde_cfg->vbif[i].dynamic_ot_wr_tbl.cfg);
}
+
+ for (i = 0; i < sde_cfg->vp_count; i++) {
+ list_for_each_entry_safe(vp_sub, vp_sub_next,
+ &sde_cfg->vp[i].sub_blks, pipeid_list) {
+ list_del(&vp_sub->pipeid_list);
+ kfree(vp_sub);
+ }
+ }
+
kfree(sde_cfg);
}
/*************************************************************
* hardware catalog init
*************************************************************/
-struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev)
+struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev,
+ u32 hw_rev)
{
int rc;
struct sde_mdss_cfg *sde_cfg;
@@ -1996,6 +2105,10 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev)
if (rc)
goto end;
+ rc = _sde_vp_parse_dt(np, sde_cfg);
+ if (rc)
+ SDE_DEBUG("virtual plane is not supported.\n");
+
sde_hardware_caps(sde_cfg, hw_rev);
return sde_cfg;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index a8f9169aaf35..bca221d2a959 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -57,6 +57,8 @@
#define SDE_COLOR_PROCESS_MAJOR(version) (((version) & 0xFFFF0000) >> 16)
#define SDE_COLOR_PROCESS_MINOR(version) ((version) & 0xFFFF)
+#define SSPP_NAME_SIZE 12
+
/**
* MDP TOP BLOCK features
* @SDE_MDP_PANIC_PER_PIPE Panic configuration needs to be be done per pipe
@@ -455,12 +457,14 @@ struct sde_ctl_cfg {
* @sblk: SSPP sub-blocks information
* @xin_id: bus client identifier
* @clk_ctrl clock control identifier
+ *@name source pipe name
*/
struct sde_sspp_cfg {
SDE_HW_BLK_INFO;
const struct sde_sspp_sub_blks *sblk;
u32 xin_id;
enum sde_clk_ctrl_type clk_ctrl;
+ char name[SSPP_NAME_SIZE];
};
/**
@@ -608,6 +612,31 @@ struct sde_perf_cfg {
};
/**
+* struct sde_vp_sub_blks - Virtual Plane sub-blocks
+* @pipeid_list list for hw pipe id
+* @sspp_id SSPP ID, refer to enum sde_sspp.
+*/
+struct sde_vp_sub_blks {
+ struct list_head pipeid_list;
+ u32 sspp_id;
+};
+
+/**
+* struct sde_vp_cfg - information of Virtual Plane SW blocks
+* @id enum identifying this block
+* @sub_blks list head for virtual plane sub blocks
+* @plane_type plane type, such as primary, overlay or cursor
+* @display_type which display the plane bound to, such as primary,
+* secondary or tertiary
+*/
+struct sde_vp_cfg {
+ u32 id;
+ struct list_head sub_blks;
+ const char *plane_type;
+ const char *display_type;
+};
+
+/**
* struct sde_mdss_cfg - information of MDSS HW
* This is the main catalog data structure representing
* this HW version. Contains number of instances,
@@ -672,6 +701,9 @@ struct sde_mdss_cfg {
/* Add additional block data structures here */
struct sde_perf_cfg perf;
+
+ u32 vp_count;
+ struct sde_vp_cfg vp[MAX_BLOCKS];
};
struct sde_mdss_hw_cfg_handler {
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 709c9970b357..45a87456e5ec 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -797,6 +797,16 @@ static void _sde_kms_drm_obj_destroy(struct sde_kms *sde_kms)
_sde_kms_release_displays(sde_kms);
}
+static inline int sde_get_crtc_id(const char *display_type)
+{
+ if (!strcmp(display_type, "primary"))
+ return 0;
+ else if (!strcmp(display_type, "secondary"))
+ return 1;
+ else
+ return 2;
+}
+
static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms)
{
struct drm_device *dev;
@@ -829,28 +839,57 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms)
(void)_sde_kms_setup_displays(dev, priv, sde_kms);
max_crtc_count = min(catalog->mixer_count, priv->num_encoders);
- max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES);
/* Create the planes */
primary_planes_idx = 0;
- for (i = 0; i < max_plane_count; i++) {
- bool primary = true;
-
- if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR)
- || primary_planes_idx >= max_crtc_count)
- primary = false;
-
- plane = sde_plane_init(dev, catalog->sspp[i].id, primary,
- (1UL << max_crtc_count) - 1);
- if (IS_ERR(plane)) {
- SDE_ERROR("sde_plane_init failed\n");
- ret = PTR_ERR(plane);
- goto fail;
+ if (catalog->vp_count) {
+ max_plane_count = min_t(u32, catalog->vp_count, MAX_PLANES);
+
+ for (i = 0; i < max_plane_count; i++) {
+ bool primary = true;
+ int crtc_id =
+ sde_get_crtc_id(catalog->vp[i].display_type);
+
+ if (strcmp(catalog->vp[i].plane_type, "primary"))
+ primary = false;
+
+ plane = sde_plane_init(dev, catalog->vp[i].id,
+ primary, 1UL << crtc_id, true);
+ if (IS_ERR(plane)) {
+ SDE_ERROR("sde_plane_init failed\n");
+ ret = PTR_ERR(plane);
+ goto fail;
+ }
+ priv->planes[priv->num_planes++] = plane;
+
+ if (primary) {
+ primary_planes[crtc_id] = plane;
+ primary_planes_idx++;
+ }
+ }
+ } else {
+ max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES);
+
+ for (i = 0; i < max_plane_count; i++) {
+ bool primary = true;
+
+ if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR)
+ || primary_planes_idx >= max_crtc_count)
+ primary = false;
+
+ plane = sde_plane_init(dev, catalog->sspp[i].id,
+ primary, (1UL << max_crtc_count) - 1,
+ false);
+ if (IS_ERR(plane)) {
+ SDE_ERROR("sde_plane_init failed\n");
+ ret = PTR_ERR(plane);
+ goto fail;
+ }
+ priv->planes[priv->num_planes++] = plane;
+
+ if (primary)
+ primary_planes[primary_planes_idx++] = plane;
}
- priv->planes[priv->num_planes++] = plane;
-
- if (primary)
- primary_planes[primary_planes_idx++] = plane;
}
max_crtc_count = min(max_crtc_count, primary_planes_idx);
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 114acfd7a173..85ebff08761b 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -78,20 +78,22 @@ enum sde_plane_qos {
};
/*
- * struct sde_plane - local sde plane structure
+ * struct sde_phy_plane - physical plane structure
+ * @sde_plane: Points to virtual plane
+ * @phy_plane_list: list of hw pipe(physical plane)
+ * @index: index of physical plane (starts from 0, order from left to right)
+ * @features: capabilities from catalog
* @csc_cfg: Decoded user configuration for csc
* @csc_usr_ptr: Points to csc_cfg if valid user config available
* @csc_ptr: Points to sde_csc_cfg structure to use for current
*/
-struct sde_plane {
- struct drm_plane base;
-
- struct msm_gem_address_space *aspace;
-
- struct mutex lock;
-
+struct sde_phy_plane {
+ struct sde_plane *sde_plane;
+ struct list_head phy_plane_list;
enum sde_sspp pipe;
- uint32_t features; /* capabilities from catalog */
+ uint32_t index;
+
+ uint32_t features;
uint32_t nformats;
uint32_t formats[64];
@@ -101,7 +103,6 @@ struct sde_plane {
struct sde_hw_scaler3_cfg *scaler3_cfg;
struct sde_hw_pipe_qos_cfg pipe_qos_cfg;
uint32_t color_fill;
- bool is_error;
bool is_rt_pipe;
struct sde_hw_pixel_ext pixel_ext;
@@ -112,9 +113,22 @@ struct sde_plane {
struct sde_csc_cfg *csc_ptr;
const struct sde_sspp_sub_blks *pipe_sblk;
+};
+/*
+ * struct sde_plane - local sde plane structure
+ */
+struct sde_plane {
+ struct drm_plane base;
+
+ struct msm_gem_address_space *aspace;
+ struct mutex lock;
+ bool is_error;
char pipe_name[SDE_NAME_SIZE];
+ struct list_head phy_plane_head;
+ u32 num_of_phy_planes;
+
struct msm_property_info property_info;
struct msm_property_data property_data[PLANE_PROP_COUNT];
struct drm_property_blob *blob_info;
@@ -133,6 +147,20 @@ static bool sde_plane_enabled(struct drm_plane_state *state)
return state && state->fb && state->crtc;
}
+static struct sde_kms *_sde_plane_get_kms(struct drm_plane *plane)
+{
+ struct msm_drm_private *priv;
+
+ if (!plane || !plane->dev)
+ return NULL;
+
+ priv = plane->dev->dev_private;
+ if (!priv)
+ return NULL;
+
+ return to_sde_kms(priv->kms);
+}
+
/**
* _sde_plane_calc_fill_level - calculate fill level of the given source format
* @plane: Pointer to drm plane
@@ -140,20 +168,20 @@ static bool sde_plane_enabled(struct drm_plane_state *state)
* @src_wdith: width of source buffer
* Return: fill level corresponding to the source buffer/format or 0 if error
*/
-static inline int _sde_plane_calc_fill_level(struct drm_plane *plane,
+static inline int _sde_plane_calc_fill_level(struct sde_phy_plane *pp,
const struct sde_format *fmt, u32 src_width)
{
struct sde_plane *psde;
u32 fixed_buff_size;
u32 total_fl;
- if (!plane || !fmt) {
+ if (!pp || !fmt) {
SDE_ERROR("invalid arguments\n");
return 0;
}
- psde = to_sde_plane(plane);
- fixed_buff_size = psde->pipe_sblk->pixel_ram_size;
+ psde = pp->sde_plane;
+ fixed_buff_size = pp->pipe_sblk->pixel_ram_size;
if (fmt->fetch_planes == SDE_PLANE_PSEUDO_PLANAR) {
if (fmt->chroma_sample == SDE_CHROMA_420) {
@@ -171,7 +199,7 @@ static inline int _sde_plane_calc_fill_level(struct drm_plane *plane,
}
SDE_DEBUG("plane%u: pnum:%d fmt:%x w:%u fl:%u\n",
- plane->base.id, psde->pipe - SSPP_VIG0,
+ psde->base.base.id, pp->pipe - SSPP_VIG0,
fmt->base.pixel_format, src_width, total_fl);
return total_fl;
@@ -236,7 +264,7 @@ static inline u32 _sde_plane_get_qos_lut_macrotile(u32 total_fl)
* @plane: Pointer to drm plane
* @fb: Pointer to framebuffer associated with the given plane
*/
-static void _sde_plane_set_qos_lut(struct drm_plane *plane,
+static void _sde_plane_set_qos_lut(struct sde_phy_plane *pp,
struct drm_framebuffer *fb)
{
struct sde_plane *psde;
@@ -244,30 +272,30 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane,
u32 qos_lut;
u32 total_fl = 0;
- if (!plane || !fb) {
- SDE_ERROR("invalid arguments plane %d fb %d\n",
- plane != 0, fb != 0);
+ if (!pp || !fb) {
+ SDE_ERROR("invalid arguments phy_plane %d fb %d\n",
+ pp != NULL, fb != NULL);
return;
}
- psde = to_sde_plane(plane);
+ psde = pp->sde_plane;
- if (!psde->pipe_hw || !psde->pipe_sblk) {
+ if (!pp->pipe_hw || !pp->pipe_sblk) {
SDE_ERROR("invalid arguments\n");
return;
- } else if (!psde->pipe_hw->ops.setup_creq_lut) {
+ } else if (!pp->pipe_hw->ops.setup_creq_lut) {
return;
}
- if (!psde->is_rt_pipe) {
- qos_lut = psde->pipe_sblk->creq_lut_nrt;
+ if (!pp->is_rt_pipe) {
+ qos_lut = pp->pipe_sblk->creq_lut_nrt;
} else {
fmt = sde_get_sde_format_ext(
fb->pixel_format,
fb->modifier,
drm_format_num_planes(fb->pixel_format));
- total_fl = _sde_plane_calc_fill_level(plane, fmt,
- psde->pipe_cfg.src_rect.w);
+ total_fl = _sde_plane_calc_fill_level(pp, fmt,
+ pp->pipe_cfg.src_rect.w);
if (SDE_FORMAT_IS_LINEAR(fmt))
qos_lut = _sde_plane_get_qos_lut_linear(total_fl);
@@ -275,20 +303,20 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane,
qos_lut = _sde_plane_get_qos_lut_macrotile(total_fl);
}
- psde->pipe_qos_cfg.creq_lut = qos_lut;
+ pp->pipe_qos_cfg.creq_lut = qos_lut;
- trace_sde_perf_set_qos_luts(psde->pipe - SSPP_VIG0,
+ trace_sde_perf_set_qos_luts(pp->pipe - SSPP_VIG0,
(fmt) ? fmt->base.pixel_format : 0,
- psde->is_rt_pipe, total_fl, qos_lut,
+ pp->is_rt_pipe, total_fl, qos_lut,
(fmt) ? SDE_FORMAT_IS_LINEAR(fmt) : 0);
SDE_DEBUG("plane%u: pnum:%d fmt:%x rt:%d fl:%u lut:0x%x\n",
- plane->base.id,
- psde->pipe - SSPP_VIG0,
+ psde->base.base.id,
+ pp->pipe - SSPP_VIG0,
(fmt) ? fmt->base.pixel_format : 0,
- psde->is_rt_pipe, total_fl, qos_lut);
+ pp->is_rt_pipe, total_fl, qos_lut);
- psde->pipe_hw->ops.setup_creq_lut(psde->pipe_hw, &psde->pipe_qos_cfg);
+ pp->pipe_hw->ops.setup_creq_lut(pp->pipe_hw, &pp->pipe_qos_cfg);
}
/**
@@ -296,30 +324,30 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane,
* @plane: Pointer to drm plane
* @fb: Pointer to framebuffer associated with the given plane
*/
-static void _sde_plane_set_danger_lut(struct drm_plane *plane,
+static void _sde_plane_set_danger_lut(struct sde_phy_plane *pp,
struct drm_framebuffer *fb)
{
struct sde_plane *psde;
const struct sde_format *fmt = NULL;
u32 danger_lut, safe_lut;
- if (!plane || !fb) {
+ if (!pp || !fb) {
SDE_ERROR("invalid arguments\n");
return;
}
- psde = to_sde_plane(plane);
+ psde = pp->sde_plane;
- if (!psde->pipe_hw || !psde->pipe_sblk) {
+ if (!pp->pipe_hw || !pp->pipe_sblk) {
SDE_ERROR("invalid arguments\n");
return;
- } else if (!psde->pipe_hw->ops.setup_danger_safe_lut) {
+ } else if (!pp->pipe_hw->ops.setup_danger_safe_lut) {
return;
}
- if (!psde->is_rt_pipe) {
- danger_lut = psde->pipe_sblk->danger_lut_nrt;
- safe_lut = psde->pipe_sblk->safe_lut_nrt;
+ if (!pp->is_rt_pipe) {
+ danger_lut = pp->pipe_sblk->danger_lut_nrt;
+ safe_lut = pp->pipe_sblk->safe_lut_nrt;
} else {
fmt = sde_get_sde_format_ext(
fb->pixel_format,
@@ -327,33 +355,33 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane,
drm_format_num_planes(fb->pixel_format));
if (SDE_FORMAT_IS_LINEAR(fmt)) {
- danger_lut = psde->pipe_sblk->danger_lut_linear;
- safe_lut = psde->pipe_sblk->safe_lut_linear;
+ danger_lut = pp->pipe_sblk->danger_lut_linear;
+ safe_lut = pp->pipe_sblk->safe_lut_linear;
} else {
- danger_lut = psde->pipe_sblk->danger_lut_tile;
- safe_lut = psde->pipe_sblk->safe_lut_tile;
+ danger_lut = pp->pipe_sblk->danger_lut_tile;
+ safe_lut = pp->pipe_sblk->safe_lut_tile;
}
}
- psde->pipe_qos_cfg.danger_lut = danger_lut;
- psde->pipe_qos_cfg.safe_lut = safe_lut;
+ pp->pipe_qos_cfg.danger_lut = danger_lut;
+ pp->pipe_qos_cfg.safe_lut = safe_lut;
- trace_sde_perf_set_danger_luts(psde->pipe - SSPP_VIG0,
+ trace_sde_perf_set_danger_luts(pp->pipe - SSPP_VIG0,
(fmt) ? fmt->base.pixel_format : 0,
(fmt) ? fmt->fetch_mode : 0,
- psde->pipe_qos_cfg.danger_lut,
- psde->pipe_qos_cfg.safe_lut);
+ pp->pipe_qos_cfg.danger_lut,
+ pp->pipe_qos_cfg.safe_lut);
SDE_DEBUG("plane%u: pnum:%d fmt:%x mode:%d luts[0x%x, 0x%x]\n",
- plane->base.id,
- psde->pipe - SSPP_VIG0,
+ psde->base.base.id,
+ pp->pipe - SSPP_VIG0,
fmt ? fmt->base.pixel_format : 0,
fmt ? fmt->fetch_mode : -1,
- psde->pipe_qos_cfg.danger_lut,
- psde->pipe_qos_cfg.safe_lut);
+ pp->pipe_qos_cfg.danger_lut,
+ pp->pipe_qos_cfg.safe_lut);
- psde->pipe_hw->ops.setup_danger_safe_lut(psde->pipe_hw,
- &psde->pipe_qos_cfg);
+ pp->pipe_hw->ops.setup_danger_safe_lut(pp->pipe_hw,
+ &pp->pipe_qos_cfg);
}
/**
@@ -362,85 +390,90 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane,
* @enable: true to enable QoS control
* @flags: QoS control mode (enum sde_plane_qos)
*/
-static void _sde_plane_set_qos_ctrl(struct drm_plane *plane,
+static void _sde_plane_set_qos_ctrl(struct sde_phy_plane *pp,
bool enable, u32 flags)
{
struct sde_plane *psde;
- if (!plane) {
+ if (!pp) {
SDE_ERROR("invalid arguments\n");
return;
}
- psde = to_sde_plane(plane);
+ psde = pp->sde_plane;
- if (!psde->pipe_hw || !psde->pipe_sblk) {
+ if (!pp->pipe_hw || !pp->pipe_sblk) {
SDE_ERROR("invalid arguments\n");
return;
- } else if (!psde->pipe_hw->ops.setup_qos_ctrl) {
+ } else if (!pp->pipe_hw->ops.setup_qos_ctrl) {
return;
}
if (flags & SDE_PLANE_QOS_VBLANK_CTRL) {
- psde->pipe_qos_cfg.creq_vblank = psde->pipe_sblk->creq_vblank;
- psde->pipe_qos_cfg.danger_vblank =
- psde->pipe_sblk->danger_vblank;
- psde->pipe_qos_cfg.vblank_en = enable;
+ pp->pipe_qos_cfg.creq_vblank = pp->pipe_sblk->creq_vblank;
+ pp->pipe_qos_cfg.danger_vblank =
+ pp->pipe_sblk->danger_vblank;
+ pp->pipe_qos_cfg.vblank_en = enable;
}
if (flags & SDE_PLANE_QOS_VBLANK_AMORTIZE) {
/* this feature overrules previous VBLANK_CTRL */
- psde->pipe_qos_cfg.vblank_en = false;
- psde->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */
+ pp->pipe_qos_cfg.vblank_en = false;
+ pp->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */
}
if (flags & SDE_PLANE_QOS_PANIC_CTRL)
- psde->pipe_qos_cfg.danger_safe_en = enable;
+ pp->pipe_qos_cfg.danger_safe_en = enable;
- if (!psde->is_rt_pipe) {
- psde->pipe_qos_cfg.vblank_en = false;
- psde->pipe_qos_cfg.danger_safe_en = false;
+ if (!pp->is_rt_pipe) {
+ pp->pipe_qos_cfg.vblank_en = false;
+ pp->pipe_qos_cfg.danger_safe_en = false;
}
SDE_DEBUG("plane%u: pnum:%d ds:%d vb:%d pri[0x%x, 0x%x] is_rt:%d\n",
- plane->base.id,
- psde->pipe - SSPP_VIG0,
- psde->pipe_qos_cfg.danger_safe_en,
- psde->pipe_qos_cfg.vblank_en,
- psde->pipe_qos_cfg.creq_vblank,
- psde->pipe_qos_cfg.danger_vblank,
- psde->is_rt_pipe);
-
- psde->pipe_hw->ops.setup_qos_ctrl(psde->pipe_hw,
- &psde->pipe_qos_cfg);
+ psde->base.base.id,
+ pp->pipe - SSPP_VIG0,
+ pp->pipe_qos_cfg.danger_safe_en,
+ pp->pipe_qos_cfg.vblank_en,
+ pp->pipe_qos_cfg.creq_vblank,
+ pp->pipe_qos_cfg.danger_vblank,
+ pp->is_rt_pipe);
+
+ pp->pipe_hw->ops.setup_qos_ctrl(pp->pipe_hw,
+ &pp->pipe_qos_cfg);
}
-int sde_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable)
+static int sde_plane_danger_signal_ctrl(struct sde_phy_plane *pp, bool enable)
{
struct sde_plane *psde;
struct msm_drm_private *priv;
struct sde_kms *sde_kms;
- if (!plane || !plane->dev) {
+ if (!pp) {
SDE_ERROR("invalid arguments\n");
return -EINVAL;
}
+ psde = pp->sde_plane;
- priv = plane->dev->dev_private;
+ if (!psde->base.dev) {
+ SDE_ERROR("invalid arguments\n");
+ return -EINVAL;
+ }
+
+ priv = psde->base.dev->dev_private;
if (!priv || !priv->kms) {
SDE_ERROR("invalid KMS reference\n");
return -EINVAL;
}
sde_kms = to_sde_kms(priv->kms);
- psde = to_sde_plane(plane);
- if (!psde->is_rt_pipe)
+ if (!pp->is_rt_pipe)
goto end;
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
- _sde_plane_set_qos_ctrl(plane, enable, SDE_PLANE_QOS_PANIC_CTRL);
+ _sde_plane_set_qos_ctrl(pp, enable, SDE_PLANE_QOS_PANIC_CTRL);
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
@@ -453,7 +486,7 @@ end:
* @plane: Pointer to drm plane
* @crtc: Pointer to drm crtc
*/
-static void _sde_plane_set_ot_limit(struct drm_plane *plane,
+static void _sde_plane_set_ot_limit(struct sde_phy_plane *pp,
struct drm_crtc *crtc)
{
struct sde_plane *psde;
@@ -461,34 +494,38 @@ static void _sde_plane_set_ot_limit(struct drm_plane *plane,
struct msm_drm_private *priv;
struct sde_kms *sde_kms;
- if (!plane || !plane->dev || !crtc) {
- SDE_ERROR("invalid arguments plane %d crtc %d\n",
- plane != 0, crtc != 0);
+ if (!pp || !crtc) {
+ SDE_ERROR("invalid arguments phy_plane %d crtc %d\n",
+ pp != NULL, crtc != NULL);
+ return;
+ }
+ psde = pp->sde_plane;
+ if (!psde->base.dev) {
+ SDE_ERROR("invalid DRM device\n");
return;
}
- priv = plane->dev->dev_private;
+ priv = psde->base.dev->dev_private;
if (!priv || !priv->kms) {
SDE_ERROR("invalid KMS reference\n");
return;
}
sde_kms = to_sde_kms(priv->kms);
- psde = to_sde_plane(plane);
- if (!psde->pipe_hw) {
+ if (!pp->pipe_hw) {
SDE_ERROR("invalid pipe reference\n");
return;
}
memset(&ot_params, 0, sizeof(ot_params));
- ot_params.xin_id = psde->pipe_hw->cap->xin_id;
- ot_params.num = psde->pipe_hw->idx - SSPP_NONE;
- ot_params.width = psde->pipe_cfg.src_rect.w;
- ot_params.height = psde->pipe_cfg.src_rect.h;
- ot_params.is_wfd = !psde->is_rt_pipe;
+ ot_params.xin_id = pp->pipe_hw->cap->xin_id;
+ ot_params.num = pp->pipe_hw->idx - SSPP_NONE;
+ ot_params.width = pp->pipe_cfg.src_rect.w;
+ ot_params.height = pp->pipe_cfg.src_rect.h;
+ ot_params.is_wfd = !pp->is_rt_pipe;
ot_params.frame_rate = crtc->mode.vrefresh;
ot_params.vbif_idx = VBIF_RT;
- ot_params.clk_ctrl = psde->pipe_hw->cap->clk_ctrl;
+ ot_params.clk_ctrl = pp->pipe_hw->cap->clk_ctrl;
ot_params.rd = true;
sde_vbif_set_ot_limit(sde_kms, &ot_params);
@@ -559,40 +596,97 @@ int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms)
return ret;
}
-static inline void _sde_plane_set_scanout(struct drm_plane *plane,
+/**
+ * _sde_plane_get_aspace: gets the address space based on the
+ * fb_translation mode property
+ */
+static int _sde_plane_get_aspace(
+ struct sde_plane *psde,
+ struct sde_plane_state *pstate,
+ struct msm_gem_address_space **aspace)
+{
+ struct sde_kms *kms;
+ int mode;
+
+ if (!psde || !pstate || !aspace) {
+ SDE_ERROR("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ kms = _sde_plane_get_kms(&psde->base);
+ if (!kms) {
+ SDE_ERROR("invalid kms\n");
+ return -EINVAL;
+ }
+
+ mode = sde_plane_get_property(pstate,
+ PLANE_PROP_FB_TRANSLATION_MODE);
+
+ switch (mode) {
+ case SDE_DRM_FB_NON_SEC:
+ *aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE];
+ if (!aspace)
+ return -EINVAL;
+ break;
+ case SDE_DRM_FB_SEC:
+ *aspace = kms->aspace[MSM_SMMU_DOMAIN_SECURE];
+ if (!aspace)
+ return -EINVAL;
+ break;
+ case SDE_DRM_FB_SEC_DIR_TRANS:
+ case SDE_DRM_FB_NON_SEC_DIR_TRANS:
+ *aspace = NULL;
+ break;
+ default:
+ SDE_ERROR("invalid fb_translation mode:%d\n", mode);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static inline void _sde_plane_set_scanout(struct sde_phy_plane *pp,
struct sde_plane_state *pstate,
struct sde_hw_pipe_cfg *pipe_cfg,
struct drm_framebuffer *fb)
{
struct sde_plane *psde;
+ struct msm_gem_address_space *aspace = NULL;
int ret;
- if (!plane || !pstate || !pipe_cfg || !fb) {
+ if (!pp || !pstate || !pipe_cfg || !fb) {
SDE_ERROR(
- "invalid arg(s), plane %d state %d cfg %d fb %d\n",
- plane != 0, pstate != 0, pipe_cfg != 0, fb != 0);
+ "invalid arg(s), phy_plane %d state %d cfg %d fb %d\n",
+ pp != 0, pstate != 0, pipe_cfg != 0, fb != 0);
return;
}
- psde = to_sde_plane(plane);
- if (!psde->pipe_hw) {
+ psde = pp->sde_plane;
+ if (!pp->pipe_hw) {
SDE_ERROR_PLANE(psde, "invalid pipe_hw\n");
return;
}
- ret = sde_format_populate_layout(psde->aspace, fb, &pipe_cfg->layout);
+ ret = _sde_plane_get_aspace(psde, pstate, &aspace);
+ if (ret) {
+ SDE_ERROR_PLANE(psde, "Failed to get aspace %d\n", ret);
+ return;
+ }
+
+ ret = sde_format_populate_layout(aspace, fb, &pipe_cfg->layout);
if (ret == -EAGAIN)
SDE_DEBUG_PLANE(psde, "not updating same src addrs\n");
else if (ret)
SDE_ERROR_PLANE(psde, "failed to get format layout, %d\n", ret);
- else if (psde->pipe_hw->ops.setup_sourceaddress)
- psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg);
+ else if (pp->pipe_hw && pp->pipe_hw->ops.setup_sourceaddress)
+ pp->pipe_hw->ops.setup_sourceaddress(pp->pipe_hw, pipe_cfg);
}
-static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde,
+static int _sde_plane_setup_scaler3_lut(struct sde_phy_plane *pp,
struct sde_plane_state *pstate)
{
- struct sde_hw_scaler3_cfg *cfg = psde->scaler3_cfg;
+ struct sde_plane *psde = pp->sde_plane;
+ struct sde_hw_scaler3_cfg *cfg = pp->scaler3_cfg;
int ret = 0;
cfg->dir_lut = msm_property_get_blob(
@@ -612,7 +706,7 @@ static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde,
return ret;
}
-static void _sde_plane_setup_scaler3(struct sde_plane *psde,
+static void _sde_plane_setup_scaler3(struct sde_phy_plane *pp,
uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h,
struct sde_hw_scaler3_cfg *scale_cfg,
const struct sde_format *fmt,
@@ -620,10 +714,10 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde,
{
uint32_t decimated, i;
- if (!psde || !scale_cfg || !fmt || !chroma_subsmpl_h ||
+ if (!pp || !scale_cfg || !fmt || !chroma_subsmpl_h ||
!chroma_subsmpl_v) {
SDE_ERROR("psde %pK scale_cfg %pK fmt %pK smp_h %d smp_v %d\n"
- , psde, scale_cfg, fmt, chroma_subsmpl_h,
+ , pp, scale_cfg, fmt, chroma_subsmpl_h,
chroma_subsmpl_v);
return;
}
@@ -631,11 +725,11 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde,
memset(scale_cfg, 0, sizeof(*scale_cfg));
decimated = DECIMATED_DIMENSION(src_w,
- psde->pipe_cfg.horz_decimation);
+ pp->pipe_cfg.horz_decimation);
scale_cfg->phase_step_x[SDE_SSPP_COMP_0] =
mult_frac((1 << PHASE_STEP_SHIFT), decimated, dst_w);
decimated = DECIMATED_DIMENSION(src_h,
- psde->pipe_cfg.vert_decimation);
+ pp->pipe_cfg.vert_decimation);
scale_cfg->phase_step_y[SDE_SSPP_COMP_0] =
mult_frac((1 << PHASE_STEP_SHIFT), decimated, dst_h);
@@ -657,9 +751,9 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde,
for (i = 0; i < SDE_MAX_PLANES; i++) {
scale_cfg->src_width[i] = DECIMATED_DIMENSION(src_w,
- psde->pipe_cfg.horz_decimation);
+ pp->pipe_cfg.horz_decimation);
scale_cfg->src_height[i] = DECIMATED_DIMENSION(src_h,
- psde->pipe_cfg.vert_decimation);
+ pp->pipe_cfg.vert_decimation);
if (SDE_FORMAT_IS_YUV(fmt))
scale_cfg->src_width[i] &= ~0x1;
if (i == SDE_SSPP_COMP_1_2 || i == SDE_SSPP_COMP_2) {
@@ -668,9 +762,9 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde,
}
scale_cfg->preload_x[i] = SDE_QSEED3_DEFAULT_PRELOAD_H;
scale_cfg->preload_y[i] = SDE_QSEED3_DEFAULT_PRELOAD_V;
- psde->pixel_ext.num_ext_pxls_top[i] =
+ pp->pixel_ext.num_ext_pxls_top[i] =
scale_cfg->src_height[i];
- psde->pixel_ext.num_ext_pxls_left[i] =
+ pp->pixel_ext.num_ext_pxls_left[i] =
scale_cfg->src_width[i];
}
if (!(SDE_FORMAT_IS_YUV(fmt)) && (src_h == dst_h)
@@ -835,7 +929,7 @@ static void _sde_plane_setup_pixel_ext(struct sde_plane *psde,
}
}
-static inline void _sde_plane_setup_csc(struct sde_plane *psde)
+static inline void _sde_plane_setup_csc(struct sde_phy_plane *pp)
{
static const struct sde_csc_cfg sde_csc_YUV2RGB_601L = {
{
@@ -866,26 +960,30 @@ static inline void _sde_plane_setup_csc(struct sde_plane *psde)
{ 0x00, 0x3ff, 0x00, 0x3ff, 0x00, 0x3ff,},
};
- if (!psde) {
+ struct sde_plane *psde;
+
+ if (!pp) {
SDE_ERROR("invalid plane\n");
return;
}
+ psde = pp->sde_plane;
/* revert to kernel default if override not available */
- if (psde->csc_usr_ptr)
- psde->csc_ptr = psde->csc_usr_ptr;
- else if (BIT(SDE_SSPP_CSC_10BIT) & psde->features)
- psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc10_YUV2RGB_601L;
+ if (pp->csc_usr_ptr)
+ pp->csc_ptr = pp->csc_usr_ptr;
+ else if (BIT(SDE_SSPP_CSC_10BIT) & pp->features)
+ pp->csc_ptr = (struct sde_csc_cfg *)&sde_csc10_YUV2RGB_601L;
else
- psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L;
+ pp->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L;
SDE_DEBUG_PLANE(psde, "using 0x%X 0x%X 0x%X...\n",
- psde->csc_ptr->csc_mv[0],
- psde->csc_ptr->csc_mv[1],
- psde->csc_ptr->csc_mv[2]);
+ pp->csc_ptr->csc_mv[0],
+ pp->csc_ptr->csc_mv[1],
+ pp->csc_ptr->csc_mv[2]);
}
-static void sde_color_process_plane_setup(struct drm_plane *plane)
+static void sde_color_process_plane_setup(struct drm_plane *plane,
+ struct sde_phy_plane *pp)
{
struct sde_plane *psde;
struct sde_plane_state *pstate;
@@ -893,32 +991,32 @@ static void sde_color_process_plane_setup(struct drm_plane *plane)
struct drm_msm_memcol *memcol = NULL;
size_t memcol_sz = 0;
- psde = to_sde_plane(plane);
+ psde = pp->sde_plane;
pstate = to_sde_plane_state(plane->state);
hue = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_HUE_ADJUST);
- if (psde->pipe_hw->ops.setup_pa_hue)
- psde->pipe_hw->ops.setup_pa_hue(psde->pipe_hw, &hue);
+ if (pp->pipe_hw->ops.setup_pa_hue)
+ pp->pipe_hw->ops.setup_pa_hue(pp->pipe_hw, &hue);
saturation = (uint32_t) sde_plane_get_property(pstate,
PLANE_PROP_SATURATION_ADJUST);
- if (psde->pipe_hw->ops.setup_pa_sat)
- psde->pipe_hw->ops.setup_pa_sat(psde->pipe_hw, &saturation);
+ if (pp->pipe_hw->ops.setup_pa_sat)
+ pp->pipe_hw->ops.setup_pa_sat(pp->pipe_hw, &saturation);
value = (uint32_t) sde_plane_get_property(pstate,
PLANE_PROP_VALUE_ADJUST);
- if (psde->pipe_hw->ops.setup_pa_val)
- psde->pipe_hw->ops.setup_pa_val(psde->pipe_hw, &value);
+ if (pp->pipe_hw->ops.setup_pa_val)
+ pp->pipe_hw->ops.setup_pa_val(pp->pipe_hw, &value);
contrast = (uint32_t) sde_plane_get_property(pstate,
PLANE_PROP_CONTRAST_ADJUST);
- if (psde->pipe_hw->ops.setup_pa_cont)
- psde->pipe_hw->ops.setup_pa_cont(psde->pipe_hw, &contrast);
+ if (pp->pipe_hw->ops.setup_pa_cont)
+ pp->pipe_hw->ops.setup_pa_cont(pp->pipe_hw, &contrast);
- if (psde->pipe_hw->ops.setup_pa_memcolor) {
+ if (pp->pipe_hw->ops.setup_pa_memcolor) {
/* Skin memory color setup */
memcol = msm_property_get_blob(&psde->property_info,
pstate->property_blobs,
&memcol_sz,
PLANE_PROP_SKIN_COLOR);
- psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw,
+ pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw,
MEMCOLOR_SKIN, memcol);
/* Sky memory color setup */
@@ -926,7 +1024,7 @@ static void sde_color_process_plane_setup(struct drm_plane *plane)
pstate->property_blobs,
&memcol_sz,
PLANE_PROP_SKY_COLOR);
- psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw,
+ pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw,
MEMCOLOR_SKY, memcol);
/* Foliage memory color setup */
@@ -934,87 +1032,89 @@ static void sde_color_process_plane_setup(struct drm_plane *plane)
pstate->property_blobs,
&memcol_sz,
PLANE_PROP_FOLIAGE_COLOR);
- psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw,
+ pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw,
MEMCOLOR_FOLIAGE, memcol);
}
}
-static void _sde_plane_setup_scaler(struct sde_plane *psde,
+static void _sde_plane_setup_scaler(struct sde_phy_plane *pp,
const struct sde_format *fmt,
struct sde_plane_state *pstate)
{
struct sde_hw_pixel_ext *pe;
uint32_t chroma_subsmpl_h, chroma_subsmpl_v;
+ struct sde_plane *psde;
- if (!psde || !fmt) {
- SDE_ERROR("invalid arg(s), plane %d fmt %d state %d\n",
- psde != 0, fmt != 0, pstate != 0);
+ if (!pp || !fmt || !pstate || !pp->sde_plane) {
+ SDE_ERROR("invalid arg(s), phy_plane %d fmt %d\n",
+ pp != NULL, fmt != NULL);
return;
}
+ psde = pp->sde_plane;
- pe = &(psde->pixel_ext);
+ pe = &(pp->pixel_ext);
- psde->pipe_cfg.horz_decimation =
+ pp->pipe_cfg.horz_decimation =
sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE);
- psde->pipe_cfg.vert_decimation =
+ pp->pipe_cfg.vert_decimation =
sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE);
/* don't chroma subsample if decimating */
- chroma_subsmpl_h = psde->pipe_cfg.horz_decimation ? 1 :
+ chroma_subsmpl_h = pp->pipe_cfg.horz_decimation ? 1 :
drm_format_horz_chroma_subsampling(fmt->base.pixel_format);
- chroma_subsmpl_v = psde->pipe_cfg.vert_decimation ? 1 :
+ chroma_subsmpl_v = pp->pipe_cfg.vert_decimation ? 1 :
drm_format_vert_chroma_subsampling(fmt->base.pixel_format);
/* update scaler */
- if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
+ if (pp->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
int error;
- error = _sde_plane_setup_scaler3_lut(psde, pstate);
- if (error || !psde->pixel_ext_usr) {
+ error = _sde_plane_setup_scaler3_lut(pp, pstate);
+ if (error || !pp->pixel_ext_usr) {
memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
/* calculate default config for QSEED3 */
- _sde_plane_setup_scaler3(psde,
- psde->pipe_cfg.src_rect.w,
- psde->pipe_cfg.src_rect.h,
- psde->pipe_cfg.dst_rect.w,
- psde->pipe_cfg.dst_rect.h,
- psde->scaler3_cfg, fmt,
+ _sde_plane_setup_scaler3(pp,
+ pp->pipe_cfg.src_rect.w,
+ pp->pipe_cfg.src_rect.h,
+ pp->pipe_cfg.dst_rect.w,
+ pp->pipe_cfg.dst_rect.h,
+ pp->scaler3_cfg, fmt,
chroma_subsmpl_h, chroma_subsmpl_v);
}
- } else if (!psde->pixel_ext_usr) {
+ } else if (!pp->pixel_ext_usr) {
uint32_t deci_dim, i;
/* calculate default configuration for QSEED2 */
memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
SDE_DEBUG_PLANE(psde, "default config\n");
- deci_dim = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.w,
- psde->pipe_cfg.horz_decimation);
+ deci_dim = DECIMATED_DIMENSION(pp->pipe_cfg.src_rect.w,
+ pp->pipe_cfg.horz_decimation);
_sde_plane_setup_scaler2(psde,
deci_dim,
- psde->pipe_cfg.dst_rect.w,
+ pp->pipe_cfg.dst_rect.w,
pe->phase_step_x,
pe->horz_filter, fmt, chroma_subsmpl_h);
if (SDE_FORMAT_IS_YUV(fmt))
deci_dim &= ~0x1;
- _sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.w,
- psde->pipe_cfg.dst_rect.w, deci_dim,
+ _sde_plane_setup_pixel_ext(psde, pp->pipe_cfg.src_rect.w,
+ pp->pipe_cfg.dst_rect.w, deci_dim,
pe->phase_step_x,
pe->roi_w,
pe->num_ext_pxls_left,
pe->num_ext_pxls_right, pe->horz_filter, fmt,
chroma_subsmpl_h, 0);
- deci_dim = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.h,
- psde->pipe_cfg.vert_decimation);
+ deci_dim = DECIMATED_DIMENSION(pp->pipe_cfg.src_rect.h,
+ pp->pipe_cfg.vert_decimation);
_sde_plane_setup_scaler2(psde,
deci_dim,
- psde->pipe_cfg.dst_rect.h,
+ pp->pipe_cfg.dst_rect.h,
pe->phase_step_y,
pe->vert_filter, fmt, chroma_subsmpl_v);
- _sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.h,
- psde->pipe_cfg.dst_rect.h, deci_dim,
+ _sde_plane_setup_pixel_ext(psde, pp->pipe_cfg.src_rect.h,
+ pp->pipe_cfg.dst_rect.h, deci_dim,
pe->phase_step_y,
pe->roi_h,
pe->num_ext_pxls_top,
@@ -1052,22 +1152,22 @@ static void _sde_plane_setup_scaler(struct sde_plane *psde,
* @alpha: 8-bit fill alpha value, 255 selects 100% alpha
* Returns: 0 on success
*/
-static int _sde_plane_color_fill(struct sde_plane *psde,
+static int _sde_plane_color_fill(struct sde_phy_plane *pp,
uint32_t color, uint32_t alpha)
{
const struct sde_format *fmt;
- if (!psde) {
+ if (!pp) {
SDE_ERROR("invalid plane\n");
return -EINVAL;
}
- if (!psde->pipe_hw) {
- SDE_ERROR_PLANE(psde, "invalid plane h/w pointer\n");
+ if (!pp->pipe_hw) {
+ SDE_ERROR_PLANE(pp->sde_plane, "invalid plane h/w pointer\n");
return -EINVAL;
}
- SDE_DEBUG_PLANE(psde, "\n");
+ SDE_DEBUG_PLANE(pp->sde_plane, "\n");
/*
* select fill format to match user property expectation,
@@ -1076,35 +1176,35 @@ static int _sde_plane_color_fill(struct sde_plane *psde,
fmt = sde_get_sde_format(DRM_FORMAT_ABGR8888);
/* update sspp */
- if (fmt && psde->pipe_hw->ops.setup_solidfill) {
- psde->pipe_hw->ops.setup_solidfill(psde->pipe_hw,
+ if (fmt && pp->pipe_hw->ops.setup_solidfill) {
+ pp->pipe_hw->ops.setup_solidfill(pp->pipe_hw,
(color & 0xFFFFFF) | ((alpha & 0xFF) << 24));
/* override scaler/decimation if solid fill */
- psde->pipe_cfg.src_rect.x = 0;
- psde->pipe_cfg.src_rect.y = 0;
- psde->pipe_cfg.src_rect.w = psde->pipe_cfg.dst_rect.w;
- psde->pipe_cfg.src_rect.h = psde->pipe_cfg.dst_rect.h;
+ pp->pipe_cfg.src_rect.x = 0;
+ pp->pipe_cfg.src_rect.y = 0;
+ pp->pipe_cfg.src_rect.w = pp->pipe_cfg.dst_rect.w;
+ pp->pipe_cfg.src_rect.h = pp->pipe_cfg.dst_rect.h;
- _sde_plane_setup_scaler(psde, fmt, 0);
+ _sde_plane_setup_scaler(pp, fmt, NULL);
- if (psde->pipe_hw->ops.setup_format)
- psde->pipe_hw->ops.setup_format(psde->pipe_hw,
+ if (pp->pipe_hw->ops.setup_format)
+ pp->pipe_hw->ops.setup_format(pp->pipe_hw,
fmt, SDE_SSPP_SOLID_FILL);
- if (psde->pipe_hw->ops.setup_rects)
- psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
- &psde->pipe_cfg, &psde->pixel_ext,
- psde->scaler3_cfg);
+ if (pp->pipe_hw->ops.setup_rects)
+ pp->pipe_hw->ops.setup_rects(pp->pipe_hw,
+ &pp->pipe_cfg, &pp->pixel_ext,
+ pp->scaler3_cfg);
}
return 0;
}
static int _sde_plane_mode_set(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *state)
{
- uint32_t nplanes, src_flags;
+ uint32_t nplanes, src_flags = 0x0;
struct sde_plane *psde;
struct sde_plane_state *pstate;
const struct sde_format *fmt;
@@ -1113,6 +1213,9 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
struct sde_rect src, dst;
bool q16_data = true;
int idx;
+ struct sde_phy_plane *pp;
+ uint32_t num_of_phy_planes = 0, maxlinewidth = 0xFFFF;
+ int mode = 0;
if (!plane) {
SDE_ERROR("invalid plane\n");
@@ -1170,18 +1273,32 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
}
}
- if (pstate->dirty & SDE_PLANE_DIRTY_RECTS)
- memset(&(psde->pipe_cfg), 0, sizeof(struct sde_hw_pipe_cfg));
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ if (pstate->dirty & SDE_PLANE_DIRTY_RECTS)
+ memset(&(pp->pipe_cfg), 0,
+ sizeof(struct sde_hw_pipe_cfg));
+
+ _sde_plane_set_scanout(pp, pstate, &pp->pipe_cfg, fb);
+
+ pstate->pending = true;
- _sde_plane_set_scanout(plane, pstate, &psde->pipe_cfg, fb);
+ pp->is_rt_pipe = sde_crtc_is_rt(crtc);
+ _sde_plane_set_qos_ctrl(pp, false, SDE_PLANE_QOS_PANIC_CTRL);
+ }
/* early out if nothing dirty */
if (!pstate->dirty)
return 0;
- pstate->pending = true;
- psde->is_rt_pipe = sde_crtc_is_rt(crtc);
- _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL);
+ memset(&src, 0, sizeof(struct sde_rect));
+
+ /* update secure session flag */
+ mode = sde_plane_get_property(pstate,
+ PLANE_PROP_FB_TRANSLATION_MODE);
+ if ((mode == SDE_DRM_FB_SEC) ||
+ (mode == SDE_DRM_FB_SEC_DIR_TRANS))
+ src_flags |= SDE_SSPP_SECURE_OVERLAY_SESSION;
+
/* update roi config */
if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) {
@@ -1201,72 +1318,100 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
BIT(SDE_DRM_DEINTERLACE)) {
SDE_DEBUG_PLANE(psde, "deinterlace\n");
for (idx = 0; idx < SDE_MAX_PLANES; ++idx)
- psde->pipe_cfg.layout.plane_pitch[idx] <<= 1;
+ pp->pipe_cfg.layout.plane_pitch[idx] <<= 1;
src.h /= 2;
src.y = DIV_ROUND_UP(src.y, 2);
src.y &= ~0x1;
}
+ }
+
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ if (maxlinewidth > pp->pipe_sblk->maxlinewidth)
+ maxlinewidth = pp->pipe_sblk->maxlinewidth;
+ num_of_phy_planes++;
+ }
- psde->pipe_cfg.src_rect = src;
- psde->pipe_cfg.dst_rect = dst;
+ /*
+ * Only need to use one physical plane if plane width is still within
+ * the limitation.
+ */
+ if (maxlinewidth >= (src.x + src.w))
+ num_of_phy_planes = 1;
+
+ if (num_of_phy_planes > 1) {
+ /* Adjust width for multi-pipe */
+ src.w /= num_of_phy_planes;
+ dst.w /= num_of_phy_planes;
+ }
+
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ /* Adjust offset for multi-pipe */
+ src.x += src.w * pp->index;
+ dst.x += dst.w * pp->index;
+
+ pp->pipe_cfg.src_rect = src;
+ pp->pipe_cfg.dst_rect = dst;
/* check for color fill */
- psde->color_fill = (uint32_t)sde_plane_get_property(pstate,
+ pp->color_fill = (uint32_t)sde_plane_get_property(pstate,
PLANE_PROP_COLOR_FILL);
- if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG) {
+ if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG) {
/* skip remaining processing on color fill */
pstate->dirty = 0x0;
- } else if (psde->pipe_hw->ops.setup_rects) {
- _sde_plane_setup_scaler(psde, fmt, pstate);
+ } else if (pp->pipe_hw->ops.setup_rects) {
+ _sde_plane_setup_scaler(pp, fmt, pstate);
- psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
- &psde->pipe_cfg, &psde->pixel_ext,
- psde->scaler3_cfg);
+ pp->pipe_hw->ops.setup_rects(pp->pipe_hw,
+ &pp->pipe_cfg, &pp->pixel_ext,
+ pp->scaler3_cfg);
}
- }
- if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) &&
- psde->pipe_hw->ops.setup_format) {
- src_flags = 0x0;
+ if (((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) ||
+ (src_flags &
+ SDE_SSPP_SECURE_OVERLAY_SESSION)) &&
+ pp->pipe_hw->ops.setup_format) {
SDE_DEBUG_PLANE(psde, "rotation 0x%llX\n",
sde_plane_get_property(pstate, PLANE_PROP_ROTATION));
- if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) &
- BIT(DRM_REFLECT_X))
- src_flags |= SDE_SSPP_FLIP_LR;
- if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) &
- BIT(DRM_REFLECT_Y))
- src_flags |= SDE_SSPP_FLIP_UD;
-
- /* update format */
- psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags);
-
- /* update csc */
- if (SDE_FORMAT_IS_YUV(fmt))
- _sde_plane_setup_csc(psde);
- else
- psde->csc_ptr = 0;
- }
+ if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION)
+ & BIT(DRM_REFLECT_X))
+ src_flags |= SDE_SSPP_FLIP_LR;
+ if (sde_plane_get_property(pstate,
+ PLANE_PROP_ROTATION) & BIT(DRM_REFLECT_Y))
+ src_flags |= SDE_SSPP_FLIP_UD;
+
+ /* update format */
+ pp->pipe_hw->ops.setup_format(pp->pipe_hw,
+ fmt, src_flags);
+
+ /* update csc */
+ if (SDE_FORMAT_IS_YUV(fmt))
+ _sde_plane_setup_csc(pp);
+ else
+ pp->csc_ptr = NULL;
+ }
- sde_color_process_plane_setup(plane);
+ sde_color_process_plane_setup(plane, pp);
- /* update sharpening */
- if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) &&
- psde->pipe_hw->ops.setup_sharpening) {
- psde->sharp_cfg.strength = SHARP_STRENGTH_DEFAULT;
- psde->sharp_cfg.edge_thr = SHARP_EDGE_THR_DEFAULT;
- psde->sharp_cfg.smooth_thr = SHARP_SMOOTH_THR_DEFAULT;
- psde->sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT;
+ /* update sharpening */
+ if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) &&
+ pp->pipe_hw->ops.setup_sharpening) {
+ pp->sharp_cfg.strength = SHARP_STRENGTH_DEFAULT;
+ pp->sharp_cfg.edge_thr = SHARP_EDGE_THR_DEFAULT;
+ pp->sharp_cfg.smooth_thr = SHARP_SMOOTH_THR_DEFAULT;
+ pp->sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT;
- psde->pipe_hw->ops.setup_sharpening(psde->pipe_hw,
- &psde->sharp_cfg);
- }
+ pp->pipe_hw->ops.setup_sharpening(pp->pipe_hw,
+ &pp->sharp_cfg);
+ }
- _sde_plane_set_qos_lut(plane, fb);
- _sde_plane_set_danger_lut(plane, fb);
+ _sde_plane_set_qos_lut(pp, fb);
+ _sde_plane_set_danger_lut(pp, fb);
- if (plane->type != DRM_PLANE_TYPE_CURSOR) {
- _sde_plane_set_qos_ctrl(plane, true, SDE_PLANE_QOS_PANIC_CTRL);
- _sde_plane_set_ot_limit(plane, crtc);
+ if (plane->type != DRM_PLANE_TYPE_CURSOR) {
+ _sde_plane_set_qos_ctrl(pp, true,
+ SDE_PLANE_QOS_PANIC_CTRL);
+ _sde_plane_set_ot_limit(pp, crtc);
+ }
}
/* clear dirty */
@@ -1280,10 +1425,23 @@ static int sde_plane_prepare_fb(struct drm_plane *plane,
{
struct drm_framebuffer *fb = new_state->fb;
struct sde_plane *psde = to_sde_plane(plane);
+ struct sde_plane_state *pstate;
+ int rc;
+
+ if (!psde || !new_state)
+ return -EINVAL;
if (!new_state->fb)
return 0;
+ pstate = to_sde_plane_state(new_state);
+ rc = _sde_plane_get_aspace(psde, pstate, &psde->aspace);
+
+ if (rc) {
+ SDE_ERROR_PLANE(psde, "Failed to get aspace %d\n", rc);
+ return rc;
+ }
+
SDE_DEBUG_PLANE(psde, "FB[%u]\n", fb->base.id);
return msm_framebuffer_prepare(fb, psde->aspace);
}
@@ -1393,10 +1551,12 @@ static int sde_plane_atomic_check(struct drm_plane *plane,
uint32_t deci_w, deci_h, src_deci_w, src_deci_h;
uint32_t max_upscale, max_downscale, min_src_size, max_linewidth;
bool q16_data = true;
+ struct sde_phy_plane *pp;
+ uint32_t num_of_phy_planes = 0;
if (!plane || !state) {
- SDE_ERROR("invalid arg(s), plane %d state %d\n",
- plane != 0, state != 0);
+ SDE_ERROR("invalid arg(s), plane %d state %d.\n",
+ plane != NULL, state != NULL);
ret = -EINVAL;
goto exit;
}
@@ -1404,11 +1564,8 @@ static int sde_plane_atomic_check(struct drm_plane *plane,
psde = to_sde_plane(plane);
pstate = to_sde_plane_state(state);
- if (!psde->pipe_sblk) {
- SDE_ERROR_PLANE(psde, "invalid catalog\n");
- ret = -EINVAL;
- goto exit;
- }
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list)
+ num_of_phy_planes++;
deci_w = sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE);
deci_h = sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE);
@@ -1422,10 +1579,6 @@ static int sde_plane_atomic_check(struct drm_plane *plane,
src_deci_w = DECIMATED_DIMENSION(src.w, deci_w);
src_deci_h = DECIMATED_DIMENSION(src.h, deci_h);
- max_upscale = psde->pipe_sblk->maxupscale;
- max_downscale = psde->pipe_sblk->maxdwnscale;
- max_linewidth = psde->pipe_sblk->maxlinewidth;
-
SDE_DEBUG_PLANE(psde, "check %d -> %d\n",
sde_plane_enabled(plane->state), sde_plane_enabled(state));
@@ -1436,73 +1589,87 @@ static int sde_plane_atomic_check(struct drm_plane *plane,
min_src_size = SDE_FORMAT_IS_YUV(fmt) ? 2 : 1;
- if (SDE_FORMAT_IS_YUV(fmt) &&
- (!(psde->features & SDE_SSPP_SCALER) ||
- !(psde->features & (BIT(SDE_SSPP_CSC)
- | BIT(SDE_SSPP_CSC_10BIT))))) {
- SDE_ERROR_PLANE(psde,
- "plane doesn't have scaler/csc for yuv\n");
- ret = -EINVAL;
-
- /* check src bounds */
- } else if (state->fb->width > MAX_IMG_WIDTH ||
- state->fb->height > MAX_IMG_HEIGHT ||
- src.w < min_src_size || src.h < min_src_size ||
- CHECK_LAYER_BOUNDS(src.x, src.w, state->fb->width) ||
- CHECK_LAYER_BOUNDS(src.y, src.h, state->fb->height)) {
- SDE_ERROR_PLANE(psde, "invalid source %u, %u, %ux%u\n",
- src.x, src.y, src.w, src.h);
- ret = -E2BIG;
-
- /* valid yuv image */
- } else if (SDE_FORMAT_IS_YUV(fmt) && ((src.x & 0x1) || (src.y & 0x1) ||
- (src.w & 0x1) || (src.h & 0x1))) {
- SDE_ERROR_PLANE(psde, "invalid yuv source %u, %u, %ux%u\n",
- src.x, src.y, src.w, src.h);
- ret = -EINVAL;
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ if (!pp->pipe_sblk) {
+ SDE_ERROR("invalid plane catalog\n");
+ ret = -EINVAL;
+ goto exit;
+ }
- /* min dst support */
- } else if (dst.w < 0x1 || dst.h < 0x1) {
- SDE_ERROR_PLANE(psde, "invalid dest rect %u, %u, %ux%u\n",
- dst.x, dst.y, dst.w, dst.h);
- ret = -EINVAL;
+ max_upscale = pp->pipe_sblk->maxupscale;
+ max_downscale = pp->pipe_sblk->maxdwnscale;
+ max_linewidth = pp->pipe_sblk->maxlinewidth;
- /* decimation validation */
- } else if (deci_w || deci_h) {
- if ((deci_w > psde->pipe_sblk->maxhdeciexp) ||
- (deci_h > psde->pipe_sblk->maxvdeciexp)) {
+ if (SDE_FORMAT_IS_YUV(fmt) &&
+ (!(pp->features & SDE_SSPP_SCALER) ||
+ !(pp->features & (BIT(SDE_SSPP_CSC)
+ | BIT(SDE_SSPP_CSC_10BIT))))) {
SDE_ERROR_PLANE(psde,
- "too much decimation requested\n");
+ "plane doesn't have scaler/csc for yuv\n");
ret = -EINVAL;
- } else if (fmt->fetch_mode != SDE_FETCH_LINEAR) {
- SDE_ERROR_PLANE(psde,
- "decimation requires linear fetch\n");
+
+ /* check src bounds */
+ } else if (state->fb->width > MAX_IMG_WIDTH ||
+ state->fb->height > MAX_IMG_HEIGHT ||
+ src.w < min_src_size || src.h < min_src_size ||
+ CHECK_LAYER_BOUNDS(src.x, src.w, state->fb->width) ||
+ CHECK_LAYER_BOUNDS(src.y, src.h, state->fb->height)) {
+ SDE_ERROR_PLANE(psde, "invalid source %u, %u, %ux%u\n",
+ src.x, src.y, src.w, src.h);
+ ret = -E2BIG;
+
+ /* valid yuv image */
+ } else if (SDE_FORMAT_IS_YUV(fmt) && ((src.x & 0x1)
+ || (src.y & 0x1) || (src.w & 0x1)
+ || (src.h & 0x1))) {
+ SDE_ERROR_PLANE(psde, "invalid yuv source %u, %u,\"\
+ %ux%u\n", src.x, src.y, src.w, src.h);
ret = -EINVAL;
- }
- } else if (!(psde->features & SDE_SSPP_SCALER) &&
- ((src.w != dst.w) || (src.h != dst.h))) {
- SDE_ERROR_PLANE(psde,
- "pipe doesn't support scaling %ux%u->%ux%u\n",
- src.w, src.h, dst.w, dst.h);
- ret = -EINVAL;
+ /* min dst support */
+ } else if (dst.w < 0x1 || dst.h < 0x1) {
+ SDE_ERROR_PLANE(psde, "invalid dest rect %u, %u,\"\
+ %ux%u\n", dst.x, dst.y, dst.w, dst.h);
+ ret = -EINVAL;
- /* check decimated source width */
- } else if (src_deci_w > max_linewidth) {
- SDE_ERROR_PLANE(psde,
- "invalid src w:%u, deci w:%u, line w:%u\n",
- src.w, src_deci_w, max_linewidth);
- ret = -E2BIG;
+ /* decimation validation */
+ } else if (deci_w || deci_h) {
+ if ((deci_w > pp->pipe_sblk->maxhdeciexp) ||
+ (deci_h > pp->pipe_sblk->maxvdeciexp)) {
+ SDE_ERROR_PLANE(psde,
+ "too much decimation requested\n");
+ ret = -EINVAL;
+ } else if (fmt->fetch_mode != SDE_FETCH_LINEAR) {
+ SDE_ERROR_PLANE(psde,
+ "decimation requires linear fetch\n");
+ ret = -EINVAL;
+ }
- /* check max scaler capability */
- } else if (((src_deci_w * max_upscale) < dst.w) ||
- ((src_deci_h * max_upscale) < dst.h) ||
- ((dst.w * max_downscale) < src_deci_w) ||
- ((dst.h * max_downscale) < src_deci_h)) {
- SDE_ERROR_PLANE(psde,
- "too much scaling requested %ux%u->%ux%u\n",
- src_deci_w, src_deci_h, dst.w, dst.h);
- ret = -E2BIG;
+ } else if (!(pp->features & SDE_SSPP_SCALER) &&
+ ((src.w != dst.w) || (src.h != dst.h))) {
+ SDE_ERROR_PLANE(psde,
+ "pipe doesn't support scaling %ux%u->%ux%u\n",
+ src.w, src.h, dst.w, dst.h);
+ ret = -EINVAL;
+
+ /* check decimated source width */
+ } else if (src_deci_w > max_linewidth * num_of_phy_planes) {
+ SDE_ERROR_PLANE(psde,
+ "invalid src w:%u, deci w:%u, line w:%u, num_phy_planes:%u\n",
+ src.w, src_deci_w, max_linewidth,
+ num_of_phy_planes);
+ ret = -E2BIG;
+
+ /* check max scaler capability */
+ } else if (((src_deci_w * max_upscale) < dst.w) ||
+ ((src_deci_h * max_upscale) < dst.h) ||
+ ((dst.w * max_downscale) < src_deci_w) ||
+ ((dst.h * max_downscale) < src_deci_h)) {
+ SDE_ERROR_PLANE(psde,
+ "too much scaling requested %ux%u->%ux%u\n",
+ src_deci_w, src_deci_h, dst.w, dst.h);
+ ret = -E2BIG;
+ }
}
modeset_update:
@@ -1519,6 +1686,7 @@ exit:
void sde_plane_flush(struct drm_plane *plane)
{
struct sde_plane *psde;
+ struct sde_phy_plane *pp;
if (!plane) {
SDE_ERROR("invalid plane\n");
@@ -1531,14 +1699,17 @@ void sde_plane_flush(struct drm_plane *plane)
* These updates have to be done immediately before the plane flush
* timing, and may not be moved to the atomic_update/mode_set functions.
*/
- if (psde->is_error)
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ if (psde->is_error)
/* force white frame with 0% alpha pipe output on error */
- _sde_plane_color_fill(psde, 0xFFFFFF, 0x0);
- else if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG)
- /* force 100% alpha */
- _sde_plane_color_fill(psde, psde->color_fill, 0xFF);
- else if (psde->pipe_hw && psde->csc_ptr && psde->pipe_hw->ops.setup_csc)
- psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr);
+ _sde_plane_color_fill(pp, 0xFFFFFF, 0x0);
+ else if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG)
+ /* force 100% alpha */
+ _sde_plane_color_fill(pp, pp->color_fill, 0xFF);
+ else if (pp->pipe_hw && pp->csc_ptr &&
+ pp->pipe_hw->ops.setup_csc)
+ pp->pipe_hw->ops.setup_csc(pp->pipe_hw, pp->csc_ptr);
+ }
/* flag h/w flush complete */
if (plane->state)
@@ -1592,25 +1763,60 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
static const struct drm_prop_enum_list e_src_config[] = {
{SDE_DRM_DEINTERLACE, "deinterlace"}
};
+ static const struct drm_prop_enum_list e_fb_translation_mode[] = {
+ {SDE_DRM_FB_NON_SEC, "non_sec"},
+ {SDE_DRM_FB_SEC, "sec"},
+ {SDE_DRM_FB_NON_SEC_DIR_TRANS, "non_sec_direct_translation"},
+ {SDE_DRM_FB_SEC_DIR_TRANS, "sec_direct_translation"},
+ };
const struct sde_format_extended *format_list;
struct sde_kms_info *info;
struct sde_plane *psde = to_sde_plane(plane);
int zpos_max = 255;
int zpos_def = 0;
char feature_name[256];
+ struct sde_phy_plane *pp;
+ uint32_t features = 0xFFFFFFFF, nformats = 64;
+ u32 maxlinewidth = -1, maxupscale = -1, maxdwnscale = -1;
+ u32 maxhdeciexp = -1, maxvdeciexp = -1;
if (!plane || !psde) {
SDE_ERROR("invalid plane\n");
return;
- } else if (!psde->pipe_hw || !psde->pipe_sblk) {
- SDE_ERROR("invalid plane, pipe_hw %d pipe_sblk %d\n",
- psde->pipe_hw != 0, psde->pipe_sblk != 0);
- return;
- } else if (!catalog) {
+ }
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ if (!pp->pipe_hw || !pp->pipe_sblk) {
+ SDE_ERROR("invalid phy_plane, pipe_hw %d\"\
+ pipe_sblk %d\n", pp->pipe_hw != NULL,
+ pp->pipe_sblk != NULL);
+ return;
+ }
+ }
+ if (!catalog) {
SDE_ERROR("invalid catalog\n");
return;
}
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ /* Get common features for all pipes */
+ features &= pp->features;
+ if (nformats > pp->nformats) {
+ nformats = pp->nformats;
+ format_list = pp->pipe_sblk->format_list;
+ }
+ if (maxlinewidth < pp->pipe_sblk->maxlinewidth)
+ maxlinewidth = pp->pipe_sblk->maxlinewidth;
+ if (maxupscale < pp->pipe_sblk->maxupscale)
+ maxupscale = pp->pipe_sblk->maxupscale;
+ if (maxdwnscale < pp->pipe_sblk->maxdwnscale)
+ maxdwnscale = pp->pipe_sblk->maxdwnscale;
+ if (maxhdeciexp < pp->pipe_sblk->maxhdeciexp)
+ maxhdeciexp = pp->pipe_sblk->maxhdeciexp;
+ if (maxvdeciexp < pp->pipe_sblk->maxvdeciexp)
+ maxvdeciexp = pp->pipe_sblk->maxvdeciexp;
+ break;
+ }
+
if (sde_is_custom_client()) {
if (catalog->mixer_count && catalog->mixer &&
catalog->mixer[0].sblk->maxblendstages) {
@@ -1633,19 +1839,24 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
msm_property_install_range(&psde->property_info, "input_fence",
0x0, 0, INR_OPEN_MAX, 0, PLANE_PROP_INPUT_FENCE);
- if (psde->pipe_sblk->maxhdeciexp) {
- msm_property_install_range(&psde->property_info, "h_decimate",
- 0x0, 0, psde->pipe_sblk->maxhdeciexp, 0,
- PLANE_PROP_H_DECIMATE);
- }
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ if (pp->pipe_sblk->maxhdeciexp) {
+ msm_property_install_range(&psde->property_info,
+ "h_decimate", 0x0, 0,
+ pp->pipe_sblk->maxhdeciexp, 0,
+ PLANE_PROP_H_DECIMATE);
+ }
- if (psde->pipe_sblk->maxvdeciexp) {
- msm_property_install_range(&psde->property_info, "v_decimate",
- 0x0, 0, psde->pipe_sblk->maxvdeciexp, 0,
+ if (pp->pipe_sblk->maxvdeciexp) {
+ msm_property_install_range(&psde->property_info,
+ "v_decimate", 0x0, 0,
+ pp->pipe_sblk->maxvdeciexp, 0,
PLANE_PROP_V_DECIMATE);
+ }
+ break;
}
- if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
+ if (features & BIT(SDE_SSPP_SCALER_QSEED3)) {
msm_property_install_volatile_range(&psde->property_info,
"scaler_v2", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V2);
msm_property_install_blob(&psde->property_info, "lut_ed", 0,
@@ -1654,38 +1865,38 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
PLANE_PROP_SCALER_LUT_CIR);
msm_property_install_blob(&psde->property_info, "lut_sep", 0,
PLANE_PROP_SCALER_LUT_SEP);
- } else if (psde->features & SDE_SSPP_SCALER) {
+ } else if (features & SDE_SSPP_SCALER) {
msm_property_install_volatile_range(&psde->property_info,
"scaler_v1", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V1);
}
- if (psde->features & BIT(SDE_SSPP_CSC)) {
+ if (features & BIT(SDE_SSPP_CSC)) {
msm_property_install_volatile_range(&psde->property_info,
"csc_v1", 0x0, 0, ~0, 0, PLANE_PROP_CSC_V1);
}
- if (psde->features & BIT(SDE_SSPP_HSIC)) {
+ if (features & BIT(SDE_SSPP_HSIC)) {
snprintf(feature_name, sizeof(feature_name), "%s%d",
"SDE_SSPP_HUE_V",
- psde->pipe_sblk->hsic_blk.version >> 16);
+ pp->pipe_sblk->hsic_blk.version >> 16);
msm_property_install_range(&psde->property_info,
feature_name, 0, 0, 0xFFFFFFFF, 0,
PLANE_PROP_HUE_ADJUST);
snprintf(feature_name, sizeof(feature_name), "%s%d",
"SDE_SSPP_SATURATION_V",
- psde->pipe_sblk->hsic_blk.version >> 16);
+ pp->pipe_sblk->hsic_blk.version >> 16);
msm_property_install_range(&psde->property_info,
feature_name, 0, 0, 0xFFFFFFFF, 0,
PLANE_PROP_SATURATION_ADJUST);
snprintf(feature_name, sizeof(feature_name), "%s%d",
"SDE_SSPP_VALUE_V",
- psde->pipe_sblk->hsic_blk.version >> 16);
+ pp->pipe_sblk->hsic_blk.version >> 16);
msm_property_install_range(&psde->property_info,
feature_name, 0, 0, 0xFFFFFFFF, 0,
PLANE_PROP_VALUE_ADJUST);
snprintf(feature_name, sizeof(feature_name), "%s%d",
"SDE_SSPP_CONTRAST_V",
- psde->pipe_sblk->hsic_blk.version >> 16);
+ pp->pipe_sblk->hsic_blk.version >> 16);
msm_property_install_range(&psde->property_info,
feature_name, 0, 0, 0xFFFFFFFF, 0,
PLANE_PROP_CONTRAST_ADJUST);
@@ -1701,9 +1912,13 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
msm_property_install_enum(&psde->property_info, "src_config", 0x0, 1,
e_src_config, ARRAY_SIZE(e_src_config), PLANE_PROP_SRC_CONFIG);
- if (psde->pipe_hw->ops.setup_solidfill)
- msm_property_install_range(&psde->property_info, "color_fill",
- 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_COLOR_FILL);
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ if (pp->pipe_hw->ops.setup_solidfill)
+ msm_property_install_range(&psde->property_info,
+ "color_fill", 0, 0, 0xFFFFFFFF, 0,
+ PLANE_PROP_COLOR_FILL);
+ break;
+ }
info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL);
if (!info) {
@@ -1715,7 +1930,6 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
DRM_MODE_PROP_IMMUTABLE, PLANE_PROP_INFO);
sde_kms_info_reset(info);
- format_list = psde->pipe_sblk->format_list;
if (format_list) {
sde_kms_info_start(info, "pixel_formats");
while (format_list->fourcc_format) {
@@ -1727,51 +1941,55 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
sde_kms_info_stop(info);
}
- sde_kms_info_add_keyint(info, "max_linewidth",
- psde->pipe_sblk->maxlinewidth);
- sde_kms_info_add_keyint(info, "max_upscale",
- psde->pipe_sblk->maxupscale);
- sde_kms_info_add_keyint(info, "max_downscale",
- psde->pipe_sblk->maxdwnscale);
- sde_kms_info_add_keyint(info, "max_horizontal_deci",
- psde->pipe_sblk->maxhdeciexp);
- sde_kms_info_add_keyint(info, "max_vertical_deci",
- psde->pipe_sblk->maxvdeciexp);
+ sde_kms_info_add_keyint(info, "max_linewidth", maxlinewidth);
+ sde_kms_info_add_keyint(info, "max_upscale", maxupscale);
+ sde_kms_info_add_keyint(info, "max_downscale", maxdwnscale);
+ sde_kms_info_add_keyint(info, "max_horizontal_deci", maxhdeciexp);
+ sde_kms_info_add_keyint(info, "max_vertical_deci", maxvdeciexp);
msm_property_set_blob(&psde->property_info, &psde->blob_info,
info->data, info->len, PLANE_PROP_INFO);
kfree(info);
- if (psde->features & BIT(SDE_SSPP_MEMCOLOR)) {
+ if (features & BIT(SDE_SSPP_MEMCOLOR)) {
snprintf(feature_name, sizeof(feature_name), "%s%d",
"SDE_SSPP_SKIN_COLOR_V",
- psde->pipe_sblk->memcolor_blk.version >> 16);
+ pp->pipe_sblk->memcolor_blk.version >> 16);
msm_property_install_blob(&psde->property_info, feature_name, 0,
PLANE_PROP_SKIN_COLOR);
snprintf(feature_name, sizeof(feature_name), "%s%d",
"SDE_SSPP_SKY_COLOR_V",
- psde->pipe_sblk->memcolor_blk.version >> 16);
+ pp->pipe_sblk->memcolor_blk.version >> 16);
msm_property_install_blob(&psde->property_info, feature_name, 0,
PLANE_PROP_SKY_COLOR);
snprintf(feature_name, sizeof(feature_name), "%s%d",
"SDE_SSPP_FOLIAGE_COLOR_V",
- psde->pipe_sblk->memcolor_blk.version >> 16);
+ pp->pipe_sblk->memcolor_blk.version >> 16);
msm_property_install_blob(&psde->property_info, feature_name, 0,
PLANE_PROP_FOLIAGE_COLOR);
}
+
+ msm_property_install_enum(&psde->property_info, "fb_translation_mode",
+ 0x0,
+ 0, e_fb_translation_mode,
+ ARRAY_SIZE(e_fb_translation_mode),
+ PLANE_PROP_FB_TRANSLATION_MODE);
}
-static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr)
+static inline void _sde_plane_set_csc_v1(struct sde_phy_plane *pp,
+ void *usr_ptr)
{
struct sde_drm_csc_v1 csc_v1;
+ struct sde_plane *psde;
int i;
- if (!psde) {
- SDE_ERROR("invalid plane\n");
+ if (!pp) {
+ SDE_ERROR("invalid phy_plane\n");
return;
}
+ psde = pp->sde_plane;
- psde->csc_usr_ptr = NULL;
+ pp->csc_usr_ptr = NULL;
if (!usr_ptr) {
SDE_DEBUG_PLANE(psde, "csc data removed\n");
return;
@@ -1784,30 +2002,33 @@ static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr)
/* populate from user space */
for (i = 0; i < SDE_CSC_MATRIX_COEFF_SIZE; ++i)
- psde->csc_cfg.csc_mv[i] = csc_v1.ctm_coeff[i] >> 16;
+ pp->csc_cfg.csc_mv[i] = csc_v1.ctm_coeff[i] >> 16;
for (i = 0; i < SDE_CSC_BIAS_SIZE; ++i) {
- psde->csc_cfg.csc_pre_bv[i] = csc_v1.pre_bias[i];
- psde->csc_cfg.csc_post_bv[i] = csc_v1.post_bias[i];
+ pp->csc_cfg.csc_pre_bv[i] = csc_v1.pre_bias[i];
+ pp->csc_cfg.csc_post_bv[i] = csc_v1.post_bias[i];
}
for (i = 0; i < SDE_CSC_CLAMP_SIZE; ++i) {
- psde->csc_cfg.csc_pre_lv[i] = csc_v1.pre_clamp[i];
- psde->csc_cfg.csc_post_lv[i] = csc_v1.post_clamp[i];
+ pp->csc_cfg.csc_pre_lv[i] = csc_v1.pre_clamp[i];
+ pp->csc_cfg.csc_post_lv[i] = csc_v1.post_clamp[i];
}
- psde->csc_usr_ptr = &psde->csc_cfg;
+ pp->csc_usr_ptr = &pp->csc_cfg;
}
-static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr)
+static inline void _sde_plane_set_scaler_v1(struct sde_phy_plane *pp,
+ void *usr)
{
struct sde_drm_scaler_v1 scale_v1;
struct sde_hw_pixel_ext *pe;
+ struct sde_plane *psde;
int i;
- if (!psde) {
- SDE_ERROR("invalid plane\n");
+ if (!pp) {
+ SDE_ERROR("invalid phy_plane\n");
return;
}
+ psde = pp->sde_plane;
- psde->pixel_ext_usr = false;
+ pp->pixel_ext_usr = false;
if (!usr) {
SDE_DEBUG_PLANE(psde, "scale data removed\n");
return;
@@ -1819,7 +2040,7 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr)
}
/* populate from user space */
- pe = &(psde->pixel_ext);
+ pe = &(pp->pixel_ext);
memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
for (i = 0; i < SDE_MAX_PLANES; i++) {
pe->init_phase_x[i] = scale_v1.init_phase_x[i];
@@ -1844,26 +2065,28 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr)
pe->roi_h[i] = scale_v1.pe.num_ext_pxls_tb[i];
}
- psde->pixel_ext_usr = true;
+ pp->pixel_ext_usr = true;
SDE_DEBUG_PLANE(psde, "user property data copied\n");
}
-static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde,
+static inline void _sde_plane_set_scaler_v2(struct sde_phy_plane *pp,
struct sde_plane_state *pstate, void *usr)
{
struct sde_drm_scaler_v2 scale_v2;
struct sde_hw_pixel_ext *pe;
int i;
struct sde_hw_scaler3_cfg *cfg;
+ struct sde_plane *psde;
- if (!psde) {
- SDE_ERROR("invalid plane\n");
+ if (!pp) {
+ SDE_ERROR("invalid phy_plane\n");
return;
}
+ psde = pp->sde_plane;
- cfg = psde->scaler3_cfg;
- psde->pixel_ext_usr = false;
+ cfg = pp->scaler3_cfg;
+ pp->pixel_ext_usr = false;
if (!usr) {
SDE_DEBUG_PLANE(psde, "scale data removed\n");
return;
@@ -1875,7 +2098,7 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde,
}
/* populate from user space */
- pe = &(psde->pixel_ext);
+ pe = &(pp->pixel_ext);
memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
cfg->enable = scale_v2.enable;
cfg->dir_en = scale_v2.dir_en;
@@ -1933,7 +2156,7 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde,
pe->btm_rpt[i] = scale_v2.pe.btm_rpt[i];
pe->roi_h[i] = scale_v2.pe.num_ext_pxls_tb[i];
}
- psde->pixel_ext_usr = true;
+ pp->pixel_ext_usr = true;
SDE_DEBUG_PLANE(psde, "user property data copied\n");
}
@@ -1945,6 +2168,7 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane,
struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL;
struct sde_plane_state *pstate;
int idx, ret = -EINVAL;
+ struct sde_phy_plane *pp;
SDE_DEBUG_PLANE(psde, "\n");
@@ -1965,14 +2189,24 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane,
_sde_plane_set_input_fence(psde, pstate, val);
break;
case PLANE_PROP_CSC_V1:
- _sde_plane_set_csc_v1(psde, (void *)val);
+ list_for_each_entry(pp, &psde->phy_plane_head,
+ phy_plane_list) {
+ _sde_plane_set_csc_v1(pp, (void *)val);
+ }
break;
case PLANE_PROP_SCALER_V1:
- _sde_plane_set_scaler_v1(psde, (void *)val);
+ list_for_each_entry(pp, &psde->phy_plane_head,
+ phy_plane_list) {
+ _sde_plane_set_scaler_v1(pp,
+ (void *)val);
+ }
break;
case PLANE_PROP_SCALER_V2:
- _sde_plane_set_scaler_v2(psde, pstate,
- (void *)val);
+ list_for_each_entry(pp, &psde->phy_plane_head,
+ phy_plane_list) {
+ _sde_plane_set_scaler_v2(pp, pstate,
+ (void *)val);
+ }
break;
default:
/* nothing to do */
@@ -2019,12 +2253,15 @@ static int sde_plane_atomic_get_property(struct drm_plane *plane,
static void sde_plane_destroy(struct drm_plane *plane)
{
struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL;
+ struct sde_phy_plane *pp, *n;
SDE_DEBUG_PLANE(psde, "\n");
if (psde) {
- _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL);
-
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ _sde_plane_set_qos_ctrl(pp,
+ false, SDE_PLANE_QOS_PANIC_CTRL);
+ }
debugfs_remove_recursive(psde->debugfs_root);
if (psde->blob_info)
@@ -2037,8 +2274,13 @@ static void sde_plane_destroy(struct drm_plane *plane)
/* this will destroy the states as well */
drm_plane_cleanup(plane);
- if (psde->pipe_hw)
- sde_hw_sspp_destroy(psde->pipe_hw);
+ list_for_each_entry_safe(pp, n,
+ &psde->phy_plane_head, phy_plane_list) {
+ if (pp->pipe_hw)
+ sde_hw_sspp_destroy(pp->pipe_hw);
+ list_del(&pp->phy_plane_list);
+ kfree(pp);
+ }
kfree(psde);
}
@@ -2174,9 +2416,22 @@ static const struct drm_plane_helper_funcs sde_plane_helper_funcs = {
.atomic_update = sde_plane_atomic_update,
};
-enum sde_sspp sde_plane_pipe(struct drm_plane *plane)
+enum sde_sspp sde_plane_pipe(struct drm_plane *plane, uint32_t index)
{
- return plane ? to_sde_plane(plane)->pipe : SSPP_NONE;
+ struct sde_plane *sde_plane = to_sde_plane(plane);
+ struct sde_phy_plane *pp;
+ int i = 0;
+ enum sde_sspp default_sspp = SSPP_NONE;
+
+ list_for_each_entry(pp, &sde_plane->phy_plane_head, phy_plane_list) {
+ if (i == 0)
+ default_sspp = pp->pipe;
+ if (i == index)
+ return pp->pipe;
+ i++;
+ }
+
+ return default_sspp;
}
static ssize_t _sde_plane_danger_read(struct file *file,
@@ -2208,10 +2463,16 @@ static ssize_t _sde_plane_danger_read(struct file *file,
static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable)
{
struct drm_plane *plane;
+ struct sde_plane *psde;
+ struct sde_phy_plane *pp;
drm_for_each_plane(plane, kms->dev) {
if (plane->fb && plane->state) {
- sde_plane_danger_signal_ctrl(plane, enable);
+ psde = to_sde_plane(plane);
+ list_for_each_entry(pp, &psde->phy_plane_head,
+ phy_plane_list) {
+ sde_plane_danger_signal_ctrl(pp, enable);
+ }
SDE_DEBUG("plane:%d img:%dx%d ",
plane->base.id, plane->fb->width,
plane->fb->height);
@@ -2229,7 +2490,7 @@ static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable)
}
static ssize_t _sde_plane_danger_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
+ const char __user *user_buf, size_t count, loff_t *ppos)
{
struct sde_kms *kms = file->private_data;
struct sde_mdss_cfg *cfg = kms->catalog;
@@ -2271,85 +2532,166 @@ static const struct file_operations sde_plane_danger_enable = {
.write = _sde_plane_danger_write,
};
-static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms)
+static void _sde_plane_init_debugfs(struct sde_plane *psde,
+ struct sde_kms *kms)
{
const struct sde_sspp_sub_blks *sblk = 0;
const struct sde_sspp_cfg *cfg = 0;
+ struct sde_phy_plane *pp;
- if (psde && psde->pipe_hw)
- cfg = psde->pipe_hw->cap;
- if (cfg)
+ if (!psde || !kms) {
+ SDE_ERROR("invalid arg(s), psde %d kms %d\n",
+ psde != NULL, kms != NULL);
+ return;
+ }
+
+ /* create overall sub-directory for the pipe */
+ psde->debugfs_root = debugfs_create_dir(psde->pipe_name,
+ sde_debugfs_get_root(kms));
+ if (!psde->debugfs_root)
+ return;
+
+ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
+ debugfs_create_u32("pipe", S_IRUGO | S_IWUSR,
+ psde->debugfs_root, &pp->pipe);
+
+ if (!pp->pipe_hw || !pp->pipe_hw->cap ||
+ !pp->pipe_hw->cap->sblk)
+ continue;
+ cfg = pp->pipe_hw->cap;
sblk = cfg->sblk;
- if (kms && sblk) {
- /* create overall sub-directory for the pipe */
- psde->debugfs_root =
- debugfs_create_dir(psde->pipe_name,
- sde_debugfs_get_root(kms));
- if (psde->debugfs_root) {
- /* don't error check these */
- debugfs_create_x32("features", S_IRUGO | S_IWUSR,
- psde->debugfs_root, &psde->features);
-
- /* add register dump support */
- sde_debugfs_setup_regset32(&psde->debugfs_src,
- sblk->src_blk.base + cfg->base,
- sblk->src_blk.len,
- kms);
- sde_debugfs_create_regset32("src_blk", S_IRUGO,
- psde->debugfs_root, &psde->debugfs_src);
-
- sde_debugfs_setup_regset32(&psde->debugfs_scaler,
- sblk->scaler_blk.base + cfg->base,
- sblk->scaler_blk.len,
- kms);
- sde_debugfs_create_regset32("scaler_blk", S_IRUGO,
- psde->debugfs_root,
- &psde->debugfs_scaler);
-
- sde_debugfs_setup_regset32(&psde->debugfs_csc,
- sblk->csc_blk.base + cfg->base,
- sblk->csc_blk.len,
- kms);
- sde_debugfs_create_regset32("csc_blk", S_IRUGO,
- psde->debugfs_root, &psde->debugfs_csc);
-
- debugfs_create_u32("xin_id",
- S_IRUGO,
- psde->debugfs_root,
- (u32 *) &cfg->xin_id);
- debugfs_create_u32("clk_ctrl",
- S_IRUGO,
- psde->debugfs_root,
- (u32 *) &cfg->clk_ctrl);
- debugfs_create_x32("creq_vblank",
- S_IRUGO | S_IWUSR,
- psde->debugfs_root,
- (u32 *) &sblk->creq_vblank);
- debugfs_create_x32("danger_vblank",
- S_IRUGO | S_IWUSR,
- psde->debugfs_root,
- (u32 *) &sblk->danger_vblank);
-
- debugfs_create_file("disable_danger",
- S_IRUGO | S_IWUSR,
- psde->debugfs_root,
- kms, &sde_plane_danger_enable);
+ /* don't error check these */
+ debugfs_create_x32("features", S_IRUGO | S_IWUSR,
+ psde->debugfs_root, &pp->features);
+
+ /* add register dump support */
+ sde_debugfs_setup_regset32(&psde->debugfs_src,
+ sblk->src_blk.base + cfg->base,
+ sblk->src_blk.len,
+ kms);
+ sde_debugfs_create_regset32("src_blk", S_IRUGO,
+ psde->debugfs_root, &psde->debugfs_src);
+
+ sde_debugfs_setup_regset32(&psde->debugfs_scaler,
+ sblk->scaler_blk.base + cfg->base,
+ sblk->scaler_blk.len,
+ kms);
+ sde_debugfs_create_regset32("scaler_blk", S_IRUGO,
+ psde->debugfs_root,
+ &psde->debugfs_scaler);
+
+ sde_debugfs_setup_regset32(&psde->debugfs_csc,
+ sblk->csc_blk.base + cfg->base,
+ sblk->csc_blk.len,
+ kms);
+ sde_debugfs_create_regset32("csc_blk", S_IRUGO,
+ psde->debugfs_root, &psde->debugfs_csc);
+
+ debugfs_create_u32("xin_id",
+ S_IRUGO,
+ psde->debugfs_root,
+ (u32 *) &cfg->xin_id);
+ debugfs_create_u32("clk_ctrl",
+ S_IRUGO,
+ psde->debugfs_root,
+ (u32 *) &cfg->clk_ctrl);
+ debugfs_create_x32("creq_vblank",
+ S_IRUGO | S_IWUSR,
+ psde->debugfs_root,
+ (u32 *) &sblk->creq_vblank);
+ debugfs_create_x32("danger_vblank",
+ S_IRUGO | S_IWUSR,
+ psde->debugfs_root,
+ (u32 *) &sblk->danger_vblank);
+
+ debugfs_create_file("disable_danger",
+ S_IRUGO | S_IWUSR,
+ psde->debugfs_root,
+ kms, &sde_plane_danger_enable);
+
+ break;
+ }
+}
+
+static int _sde_init_phy_plane(struct sde_kms *sde_kms,
+ struct sde_plane *psde, uint32_t pipe, uint32_t index,
+ struct sde_phy_plane *pp)
+{
+ int rc = 0;
+
+ pp->pipe_hw = sde_rm_get_hw_by_id(&sde_kms->rm,
+ SDE_HW_BLK_SSPP, pipe);
+ if (!pp->pipe_hw) {
+ SDE_ERROR("Not found resource for id=%d\n", pipe);
+ rc = -EINVAL;
+ goto end;
+ } else if (!pp->pipe_hw->cap || !pp->pipe_hw->cap->sblk) {
+ SDE_ERROR("[%u]SSPP returned invalid cfg\n", pipe);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* cache features mask for later */
+ pp->features = pp->pipe_hw->cap->features;
+ pp->pipe_sblk = pp->pipe_hw->cap->sblk;
+ if (!pp->pipe_sblk) {
+ SDE_ERROR("invalid sblk on pipe %d\n", pipe);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (pp->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
+ pp->scaler3_cfg = kzalloc(sizeof(struct sde_hw_scaler3_cfg),
+ GFP_KERNEL);
+ if (!pp->scaler3_cfg) {
+ SDE_ERROR("[%u]failed to allocate scale struct\n",
+ pipe);
+ rc = -ENOMEM;
+ goto end;
}
}
+
+ /* add plane to DRM framework */
+ pp->nformats = sde_populate_formats(
+ pp->pipe_sblk->format_list,
+ pp->formats,
+ NULL,
+ ARRAY_SIZE(pp->formats));
+
+ if (!pp->nformats) {
+ SDE_ERROR("[%u]no valid formats for plane\n", pipe);
+ if (pp->scaler3_cfg)
+ kzfree(pp->scaler3_cfg);
+
+ rc = -EINVAL;
+ goto end;
+ }
+
+ pp->sde_plane = psde;
+ pp->pipe = pipe;
+ pp->index = index;
+
+end:
+ return rc;
}
/* initialize plane */
struct drm_plane *sde_plane_init(struct drm_device *dev,
uint32_t pipe, bool primary_plane,
- unsigned long possible_crtcs)
+ unsigned long possible_crtcs, bool vp_enabled)
{
struct drm_plane *plane = NULL;
struct sde_plane *psde;
+ struct sde_phy_plane *pp, *n;
struct msm_drm_private *priv;
struct sde_kms *kms;
enum drm_plane_type type;
int ret = -EINVAL;
+ struct sde_vp_cfg *vp;
+ struct sde_vp_sub_blks *vp_sub;
+ uint32_t features = 0xFFFFFFFF, nformats = 64, formats[64];
+ uint32_t index = 0;
if (!dev) {
SDE_ERROR("[%u]device is NULL\n", pipe);
@@ -2383,60 +2725,76 @@ struct drm_plane *sde_plane_init(struct drm_device *dev,
/* cache local stuff for later */
plane = &psde->base;
- psde->pipe = pipe;
- psde->aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE];
- /* initialize underlying h/w driver */
- psde->pipe_hw = sde_hw_sspp_init(pipe, kms->mmio, kms->catalog);
- if (IS_ERR(psde->pipe_hw)) {
- SDE_ERROR("[%u]SSPP init failed\n", pipe);
- ret = PTR_ERR(psde->pipe_hw);
- goto clean_plane;
- } else if (!psde->pipe_hw->cap || !psde->pipe_hw->cap->sblk) {
- SDE_ERROR("[%u]SSPP init returned invalid cfg\n", pipe);
- goto clean_sspp;
- }
+ INIT_LIST_HEAD(&psde->phy_plane_head);
- /* cache features mask for later */
- psde->features = psde->pipe_hw->cap->features;
- psde->pipe_sblk = psde->pipe_hw->cap->sblk;
- if (!psde->pipe_sblk) {
- SDE_ERROR("[%u]invalid sblk\n", pipe);
- goto clean_sspp;
- }
+ /* initialize underlying h/w driver */
+ if (vp_enabled) {
+ vp = &(kms->catalog->vp[pipe]);
+ list_for_each_entry(vp_sub, &vp->sub_blks, pipeid_list) {
+ pp = kzalloc(sizeof(*pp), GFP_KERNEL);
+ if (!pp) {
+ SDE_ERROR("out of memory\n");
+ ret = -ENOMEM;
+ goto clean_plane;
+ }
- if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
- psde->scaler3_cfg = kzalloc(sizeof(struct sde_hw_scaler3_cfg),
- GFP_KERNEL);
- if (!psde->scaler3_cfg) {
- SDE_ERROR("[%u]failed to allocate scale struct\n",
- pipe);
+ ret = _sde_init_phy_plane(kms, psde, vp_sub->sspp_id,
+ index, pp);
+ if (ret) {
+ SDE_ERROR("_sde_init_phy_plane error vp=%d\n",
+ pipe);
+ kfree(pp);
+ ret = -EINVAL;
+ goto clean_plane;
+ }
+ /* Get common features for all pipes */
+ features &= pp->features;
+ if (nformats > pp->nformats) {
+ nformats = pp->nformats;
+ memcpy(formats, pp->formats,
+ sizeof(formats));
+ }
+ list_add_tail(&pp->phy_plane_list,
+ &psde->phy_plane_head);
+ index++;
+ psde->num_of_phy_planes++;
+ }
+ } else {
+ pp = kzalloc(sizeof(*pp), GFP_KERNEL);
+ if (!pp) {
+ SDE_ERROR("out of memory\n");
ret = -ENOMEM;
- goto clean_sspp;
+ goto clean_plane;
}
- }
-
- /* add plane to DRM framework */
- psde->nformats = sde_populate_formats(psde->pipe_sblk->format_list,
- psde->formats,
- 0,
- ARRAY_SIZE(psde->formats));
- if (!psde->nformats) {
- SDE_ERROR("[%u]no valid formats for plane\n", pipe);
- goto clean_sspp;
+ ret = _sde_init_phy_plane(kms, psde, pipe, index, pp);
+ if (ret) {
+ SDE_ERROR("_sde_init_phy_plane error id=%d\n",
+ pipe);
+ kfree(pp);
+ ret = -EINVAL;
+ goto clean_plane;
+ }
+ features = pp->features;
+ nformats = pp->nformats;
+ memcpy(formats, pp->formats,
+ sizeof(uint32_t) * 64);
+ list_add_tail(&pp->phy_plane_list,
+ &psde->phy_plane_head);
+ psde->num_of_phy_planes++;
}
- if (psde->features & BIT(SDE_SSPP_CURSOR))
+ if (features & BIT(SDE_SSPP_CURSOR))
type = DRM_PLANE_TYPE_CURSOR;
else if (primary_plane)
type = DRM_PLANE_TYPE_PRIMARY;
else
type = DRM_PLANE_TYPE_OVERLAY;
ret = drm_universal_plane_init(dev, plane, possible_crtcs,
- &sde_plane_funcs, psde->formats, psde->nformats, type);
+ &sde_plane_funcs, formats, nformats, type);
if (ret)
- goto clean_sspp;
+ goto clean_plane;
/* success! finalize initialization */
drm_plane_helper_add(plane, &sde_plane_helper_funcs);
@@ -2458,14 +2816,20 @@ struct drm_plane *sde_plane_init(struct drm_device *dev,
DRM_INFO("%s created for pipe %u\n", psde->pipe_name, pipe);
return plane;
-clean_sspp:
- if (psde && psde->pipe_hw)
- sde_hw_sspp_destroy(psde->pipe_hw);
-
- if (psde && psde->scaler3_cfg)
- kfree(psde->scaler3_cfg);
clean_plane:
- kfree(psde);
+ if (psde) {
+ list_for_each_entry_safe(pp, n,
+ &psde->phy_plane_head, phy_plane_list) {
+ if (pp->pipe_hw)
+ sde_hw_sspp_destroy(pp->pipe_hw);
+
+ kfree(pp->scaler3_cfg);
+ list_del(&pp->phy_plane_list);
+ kfree(pp);
+ }
+ kfree(psde);
+ }
+
exit:
return ERR_PTR(ret);
}
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index 1514f633c61e..7b91822d4cde 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -59,9 +59,10 @@ struct sde_plane_state {
/**
* sde_plane_pipe - return sspp identifier for the given plane
* @plane: Pointer to DRM plane object
+ * @index: Plane index
* Returns: sspp identifier of the given plane
*/
-enum sde_sspp sde_plane_pipe(struct drm_plane *plane);
+enum sde_sspp sde_plane_pipe(struct drm_plane *plane, uint32_t index);
/**
* sde_plane_flush - final plane operations before commit flush
@@ -75,10 +76,11 @@ void sde_plane_flush(struct drm_plane *plane);
* @pipe: sde hardware pipe identifier
* @primary_plane: true if this pipe is primary plane for crtc
* @possible_crtcs: bitmask of crtc that can be attached to the given pipe
+ * @vp_enabled: Flag indicating if virtual planes enabled
*/
struct drm_plane *sde_plane_init(struct drm_device *dev,
uint32_t pipe, bool primary_plane,
- unsigned long possible_crtcs);
+ unsigned long possible_crtcs, bool vp_enabled);
/**
* sde_plane_wait_input_fence - wait for input fence object
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c
index fca0768e2734..fe4b73b4ffea 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.c
+++ b/drivers/gpu/drm/msm/sde/sde_rm.c
@@ -23,6 +23,7 @@
#include "sde_hw_wb.h"
#include "sde_encoder.h"
#include "sde_connector.h"
+#include "sde_hw_sspp.h"
#define RESERVED_BY_OTHER(h, r) \
((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id))
@@ -197,6 +198,33 @@ bool sde_rm_get_hw(struct sde_rm *rm, struct sde_rm_hw_iter *i)
return false;
}
+void *sde_rm_get_hw_by_id(struct sde_rm *rm, enum sde_hw_blk_type type, int id)
+{
+ struct list_head *blk_list;
+ struct sde_rm_hw_blk *blk;
+ void *hw = NULL;
+
+ if (!rm || type >= SDE_HW_BLK_MAX) {
+ SDE_ERROR("invalid rm\n");
+ return hw;
+ }
+
+ blk_list = &rm->hw_blks[type];
+
+ list_for_each_entry(blk, blk_list, list) {
+ if (blk->id == id) {
+ hw = blk->hw;
+ SDE_DEBUG("found type %d %s id %d\n",
+ type, blk->type_name, blk->id);
+ return hw;
+ }
+ }
+
+ SDE_DEBUG("no match, type %d id=%d\n", type, id);
+
+ return hw;
+}
+
static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, void *hw)
{
switch (type) {
@@ -222,7 +250,8 @@ static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, void *hw)
sde_hw_wb_destroy(hw);
break;
case SDE_HW_BLK_SSPP:
- /* SSPPs are not managed by the resource manager */
+ sde_hw_sspp_destroy(hw);
+ break;
case SDE_HW_BLK_TOP:
/* Top is a singleton, not managed in hw_blks list */
case SDE_HW_BLK_MAX:
@@ -310,7 +339,9 @@ static int _sde_rm_hw_blk_create(
name = "wb";
break;
case SDE_HW_BLK_SSPP:
- /* SSPPs are not managed by the resource manager */
+ hw = sde_hw_sspp_init(id, (void __iomem *)mmio, cat);
+ name = "sspp";
+ break;
case SDE_HW_BLK_TOP:
/* Top is a singleton, not managed in hw_blks list */
case SDE_HW_BLK_MAX:
@@ -369,6 +400,13 @@ int sde_rm_init(struct sde_rm *rm,
goto fail;
}
+ for (i = 0; i < cat->sspp_count; i++) {
+ rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_SSPP,
+ cat->sspp[i].id, &cat->sspp[i]);
+ if (rc)
+ goto fail;
+ }
+
/* Interrogate HW catalog and create tracking items for hw blocks */
for (i = 0; i < cat->mixer_count; i++) {
struct sde_lm_cfg *lm = &cat->mixer[i];
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h
index 855b12ce8150..1cc22c5fbbf4 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.h
+++ b/drivers/gpu/drm/msm/sde/sde_rm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -185,6 +185,18 @@ void sde_rm_init_hw_iter(
bool sde_rm_get_hw(struct sde_rm *rm, struct sde_rm_hw_iter *iter);
/**
+ * sde_rm_get_hw_by_id - retrieve hw object given hw type and hw id
+ * Meant to do a single pass through the hardware list to iteratively
+ * retrieve hardware blocks of a given type and id.
+ * Function returns the hw resource pointer.
+ * @rm: SDE Resource Manager handle
+ * @type: hw type
+ * @id: hw id
+ * @Return: hw resource pointer on match found, NULL on no match found
+ */
+void *sde_rm_get_hw_by_id(struct sde_rm *rm, enum sde_hw_blk_type type, int id);
+
+/**
* sde_rm_check_property_topctl - validate property bitmask before it is set
* @val: user's proposed topology control bitmask
* @Return: 0 on success or error
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
index 949dc6101a58..7c0b58613747 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
@@ -130,7 +130,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode)
poll = false;
}
- if (list_empty(&therm->alarm.head) && poll)
+ if (poll)
nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm);
spin_unlock_irqrestore(&therm->lock, flags);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
index 91198d79393a..e2feccec25f5 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
@@ -83,7 +83,7 @@ nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target)
spin_unlock_irqrestore(&fan->lock, flags);
/* schedule next fan update, if not at target speed already */
- if (list_empty(&fan->alarm.head) && target != duty) {
+ if (target != duty) {
u16 bump_period = fan->bios.bump_period;
u16 slow_down_period = fan->bios.slow_down_period;
u64 delay;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
index 59701b7a6597..ff9fbe7950e5 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
@@ -53,7 +53,7 @@ nvkm_fantog_update(struct nvkm_fantog *fan, int percent)
duty = !nvkm_gpio_get(gpio, 0, DCB_GPIO_FAN, 0xff);
nvkm_gpio_set(gpio, 0, DCB_GPIO_FAN, 0xff, duty);
- if (list_empty(&fan->alarm.head) && percent != (duty * 100)) {
+ if (percent != (duty * 100)) {
u64 next_change = (percent * fan->period_us) / 100;
if (!duty)
next_change = fan->period_us - next_change;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
index b9703c02d8ca..9a79e91fdfdc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
@@ -185,7 +185,7 @@ alarm_timer_callback(struct nvkm_alarm *alarm)
spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
/* schedule the next poll in one second */
- if (therm->func->temp_get(therm) >= 0 && list_empty(&alarm->head))
+ if (therm->func->temp_get(therm) >= 0)
nvkm_timer_alarm(tmr, 1000000000ULL, alarm);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
index d4dae1f12d62..79fcdb43e174 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
@@ -36,23 +36,29 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
unsigned long flags;
LIST_HEAD(exec);
- /* move any due alarms off the pending list */
+ /* Process pending alarms. */
spin_lock_irqsave(&tmr->lock, flags);
list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
- if (alarm->timestamp <= nvkm_timer_read(tmr))
- list_move_tail(&alarm->head, &exec);
+ /* Have we hit the earliest alarm that hasn't gone off? */
+ if (alarm->timestamp > nvkm_timer_read(tmr)) {
+ /* Schedule it. If we didn't race, we're done. */
+ tmr->func->alarm_init(tmr, alarm->timestamp);
+ if (alarm->timestamp > nvkm_timer_read(tmr))
+ break;
+ }
+
+ /* Move to completed list. We'll drop the lock before
+ * executing the callback so it can reschedule itself.
+ */
+ list_move_tail(&alarm->head, &exec);
}
- /* reschedule interrupt for next alarm time */
- if (!list_empty(&tmr->alarms)) {
- alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head);
- tmr->func->alarm_init(tmr, alarm->timestamp);
- } else {
+ /* Shut down interrupt if no more pending alarms. */
+ if (list_empty(&tmr->alarms))
tmr->func->alarm_fini(tmr);
- }
spin_unlock_irqrestore(&tmr->lock, flags);
- /* execute any pending alarm handlers */
+ /* Execute completed callbacks. */
list_for_each_entry_safe(alarm, atemp, &exec, head) {
list_del_init(&alarm->head);
alarm->func(alarm);
@@ -65,24 +71,37 @@ nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
struct nvkm_alarm *list;
unsigned long flags;
- alarm->timestamp = nvkm_timer_read(tmr) + nsec;
-
- /* append new alarm to list, in soonest-alarm-first order */
+ /* Remove alarm from pending list.
+ *
+ * This both protects against the corruption of the list,
+ * and implements alarm rescheduling/cancellation.
+ */
spin_lock_irqsave(&tmr->lock, flags);
- if (!nsec) {
- if (!list_empty(&alarm->head))
- list_del(&alarm->head);
- } else {
+ list_del_init(&alarm->head);
+
+ if (nsec) {
+ /* Insert into pending list, ordered earliest to latest. */
+ alarm->timestamp = nvkm_timer_read(tmr) + nsec;
list_for_each_entry(list, &tmr->alarms, head) {
if (list->timestamp > alarm->timestamp)
break;
}
+
list_add_tail(&alarm->head, &list->head);
+
+ /* Update HW if this is now the earliest alarm. */
+ list = list_first_entry(&tmr->alarms, typeof(*list), head);
+ if (list == alarm) {
+ tmr->func->alarm_init(tmr, alarm->timestamp);
+ /* This shouldn't happen if callers aren't stupid.
+ *
+ * Worst case scenario is that it'll take roughly
+ * 4 seconds for the next alarm to trigger.
+ */
+ WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr));
+ }
}
spin_unlock_irqrestore(&tmr->lock, flags);
-
- /* process pending alarms */
- nvkm_timer_alarm_trigger(tmr);
}
void
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c
index 7b9ce87f0617..7f48249f41de 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c
@@ -76,8 +76,8 @@ nv04_timer_intr(struct nvkm_timer *tmr)
u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0);
if (stat & 0x00000001) {
- nvkm_timer_alarm_trigger(tmr);
nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001);
+ nvkm_timer_alarm_trigger(tmr);
stat &= ~0x00000001;
}
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 55f906c9cb90..b4d0656d062b 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -979,6 +979,13 @@ static void _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev)
spin_unlock(&dispatcher->plist_lock);
}
+static inline void _decrement_submit_now(struct kgsl_device *device)
+{
+ spin_lock(&device->submit_lock);
+ device->submit_now--;
+ spin_unlock(&device->submit_lock);
+}
+
/**
* adreno_dispatcher_issuecmds() - Issue commmands from pending contexts
* @adreno_dev: Pointer to the adreno device struct
@@ -988,15 +995,29 @@ static void _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev)
static void adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev)
{
struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher;
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+
+ spin_lock(&device->submit_lock);
+ /* If state transition to SLUMBER, schedule the work for later */
+ if (device->slumber == true) {
+ spin_unlock(&device->submit_lock);
+ goto done;
+ }
+ device->submit_now++;
+ spin_unlock(&device->submit_lock);
/* If the dispatcher is busy then schedule the work for later */
if (!mutex_trylock(&dispatcher->mutex)) {
- adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev));
- return;
+ _decrement_submit_now(device);
+ goto done;
}
_adreno_dispatcher_issuecmds(adreno_dev);
mutex_unlock(&dispatcher->mutex);
+ _decrement_submit_now(device);
+ return;
+done:
+ adreno_dispatcher_schedule(device);
}
/**
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index b2def8dea954..7584811f388a 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -4719,6 +4719,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
device->id, device->reg_phys, device->reg_len);
rwlock_init(&device->context_lock);
+ spin_lock_init(&device->submit_lock);
setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index d93fd9bfbcd0..64dd45a30612 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -256,6 +256,11 @@ struct kgsl_device {
struct kgsl_pwrctrl pwrctrl;
int open_count;
+ /* For GPU inline submission */
+ uint32_t submit_now;
+ spinlock_t submit_lock;
+ bool slumber;
+
struct mutex mutex;
uint32_t state;
uint32_t requested_state;
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 0150d50c925b..e42f92392e8d 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -81,6 +81,12 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device,
static void kgsl_pwrctrl_request_state(struct kgsl_device *device,
unsigned int state);
static int _isense_clk_set_rate(struct kgsl_pwrctrl *pwr, int level);
+static int kgsl_pwrctrl_clk_set_rate(struct clk *grp_clk, unsigned int freq,
+ const char *name);
+static void _gpu_clk_prepare_enable(struct kgsl_device *device,
+ struct clk *clk, const char *name);
+static void _bimc_clk_prepare_enable(struct kgsl_device *device,
+ struct clk *clk, const char *name);
/**
* _record_pwrevent() - Record the history of the new event
@@ -405,7 +411,8 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
pwrlevel = &pwr->pwrlevels[pwr->active_pwrlevel];
/* Change register settings if any BEFORE pwrlevel change*/
kgsl_pwrctrl_pwrlevel_change_settings(device, 0);
- clk_set_rate(pwr->grp_clks[0], pwrlevel->gpu_freq);
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0],
+ pwrlevel->gpu_freq, clocks[0]);
_isense_clk_set_rate(pwr, pwr->active_pwrlevel);
trace_kgsl_pwrlevel(device,
@@ -423,9 +430,12 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
if (pwr->gpu_bimc_int_clk) {
if (pwr->active_pwrlevel == 0 &&
!pwr->gpu_bimc_interface_enabled) {
- clk_set_rate(pwr->gpu_bimc_int_clk,
- pwr->gpu_bimc_int_clk_freq);
- clk_prepare_enable(pwr->gpu_bimc_int_clk);
+ kgsl_pwrctrl_clk_set_rate(pwr->gpu_bimc_int_clk,
+ pwr->gpu_bimc_int_clk_freq,
+ "bimc_gpu_clk");
+ _bimc_clk_prepare_enable(device,
+ pwr->gpu_bimc_int_clk,
+ "bimc_gpu_clk");
pwr->gpu_bimc_interface_enabled = 1;
} else if (pwr->previous_pwrlevel == 0
&& pwr->gpu_bimc_interface_enabled) {
@@ -1650,9 +1660,9 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
(requested_state != KGSL_STATE_NAP)) {
for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
clk_unprepare(pwr->grp_clks[i]);
- clk_set_rate(pwr->grp_clks[0],
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0],
pwr->pwrlevels[pwr->num_pwrlevels - 1].
- gpu_freq);
+ gpu_freq, clocks[0]);
_isense_clk_set_rate(pwr,
pwr->num_pwrlevels - 1);
}
@@ -1664,9 +1674,9 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
clk_unprepare(pwr->grp_clks[i]);
if ((pwr->pwrlevels[0].gpu_freq > 0)) {
- clk_set_rate(pwr->grp_clks[0],
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0],
pwr->pwrlevels[pwr->num_pwrlevels - 1].
- gpu_freq);
+ gpu_freq, clocks[0]);
_isense_clk_set_rate(pwr,
pwr->num_pwrlevels - 1);
}
@@ -1679,29 +1689,31 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
/* High latency clock maintenance. */
if (device->state != KGSL_STATE_NAP) {
if (pwr->pwrlevels[0].gpu_freq > 0) {
- clk_set_rate(pwr->grp_clks[0],
+ kgsl_pwrctrl_clk_set_rate(
+ pwr->grp_clks[0],
pwr->pwrlevels
[pwr->active_pwrlevel].
- gpu_freq);
+ gpu_freq, clocks[0]);
_isense_clk_set_rate(pwr,
pwr->active_pwrlevel);
}
-
- for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
- clk_prepare(pwr->grp_clks[i]);
}
- /* as last step, enable grp_clk
- this is to let GPU interrupt to come */
+
for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
- clk_enable(pwr->grp_clks[i]);
+ _gpu_clk_prepare_enable(device,
+ pwr->grp_clks[i], clocks[i]);
+
/* Enable the gpu-bimc-interface clocks */
if (pwr->gpu_bimc_int_clk) {
if (pwr->active_pwrlevel == 0 &&
!pwr->gpu_bimc_interface_enabled) {
- clk_set_rate(pwr->gpu_bimc_int_clk,
- pwr->gpu_bimc_int_clk_freq);
- clk_prepare_enable(
- pwr->gpu_bimc_int_clk);
+ kgsl_pwrctrl_clk_set_rate(
+ pwr->gpu_bimc_int_clk,
+ pwr->gpu_bimc_int_clk_freq,
+ "bimc_gpu_clk");
+ _bimc_clk_prepare_enable(device,
+ pwr->gpu_bimc_int_clk,
+ "bimc_gpu_clk");
pwr->gpu_bimc_interface_enabled = 1;
}
}
@@ -2022,7 +2034,54 @@ static int _isense_clk_set_rate(struct kgsl_pwrctrl *pwr, int level)
rate = clk_round_rate(pwr->grp_clks[pwr->isense_clk_indx],
level > pwr->isense_clk_on_level ?
KGSL_XO_CLK_FREQ : KGSL_ISENSE_CLK_FREQ);
- return clk_set_rate(pwr->grp_clks[pwr->isense_clk_indx], rate);
+ return kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[pwr->isense_clk_indx],
+ rate, clocks[pwr->isense_clk_indx]);
+}
+
+/*
+ * _gpu_clk_prepare_enable - Enable the specified GPU clock
+ * Try once to enable it and then BUG() for debug
+ */
+static void _gpu_clk_prepare_enable(struct kgsl_device *device,
+ struct clk *clk, const char *name)
+{
+ int ret;
+
+ if (device->state == KGSL_STATE_NAP) {
+ ret = clk_enable(clk);
+ if (ret)
+ goto err;
+ return;
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (!ret)
+ return;
+err:
+ /* Failure is fatal so BUG() to facilitate debug */
+ KGSL_DRV_FATAL(device, "KGSL:%s enable error:%d\n", name, ret);
+}
+
+/*
+ * _bimc_clk_prepare_enable - Enable the specified GPU clock
+ * Try once to enable it and then BUG() for debug
+ */
+static void _bimc_clk_prepare_enable(struct kgsl_device *device,
+ struct clk *clk, const char *name)
+{
+ int ret = clk_prepare_enable(clk);
+ /* Failure is fatal so BUG() to facilitate debug */
+ if (ret)
+ KGSL_DRV_FATAL(device, "KGSL:%s enable error:%d\n", name, ret);
+}
+
+static int kgsl_pwrctrl_clk_set_rate(struct clk *grp_clk, unsigned int freq,
+ const char *name)
+{
+ int ret = clk_set_rate(grp_clk, freq);
+
+ WARN(ret, "KGSL:%s set freq %d failed:%d\n", name, freq, ret);
+ return ret;
}
static inline void _close_pcl(struct kgsl_pwrctrl *pwr)
@@ -2117,11 +2176,12 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
pwr->pwrlevels[i].gpu_freq = freq;
}
- clk_set_rate(pwr->grp_clks[0],
- pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq);
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0],
+ pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq, clocks[0]);
- clk_set_rate(pwr->grp_clks[6],
- clk_round_rate(pwr->grp_clks[6], KGSL_RBBMTIMER_CLK_FREQ));
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[6],
+ clk_round_rate(pwr->grp_clks[6], KGSL_RBBMTIMER_CLK_FREQ),
+ clocks[6]);
_isense_clk_set_rate(pwr, pwr->num_pwrlevels - 1);
@@ -2347,9 +2407,24 @@ void kgsl_idle_check(struct work_struct *work)
|| device->state == KGSL_STATE_NAP) {
if (!atomic_read(&device->active_cnt)) {
+ spin_lock(&device->submit_lock);
+ if (device->submit_now) {
+ spin_unlock(&device->submit_lock);
+ goto done;
+ }
+ /* Don't allow GPU inline submission in SLUMBER */
+ if (requested_state == KGSL_STATE_SLUMBER)
+ device->slumber = true;
+ spin_unlock(&device->submit_lock);
+
ret = kgsl_pwrctrl_change_state(device,
device->requested_state);
if (ret == -EBUSY) {
+ if (requested_state == KGSL_STATE_SLUMBER) {
+ spin_lock(&device->submit_lock);
+ device->slumber = false;
+ spin_unlock(&device->submit_lock);
+ }
/*
* If the GPU is currently busy, restore
* the requested state and reschedule
@@ -2360,7 +2435,7 @@ void kgsl_idle_check(struct work_struct *work)
kgsl_schedule_work(&device->idle_check_ws);
}
}
-
+done:
if (!ret)
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
@@ -2789,6 +2864,13 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device,
trace_kgsl_pwr_set_state(device, state);
device->state = state;
device->requested_state = KGSL_STATE_NONE;
+
+ spin_lock(&device->submit_lock);
+ if (state == KGSL_STATE_SLUMBER || state == KGSL_STATE_SUSPEND)
+ device->slumber = true;
+ else
+ device->slumber = false;
+ spin_unlock(&device->submit_lock);
}
static void kgsl_pwrctrl_request_state(struct kgsl_device *device,
diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c
index e690dd11e99f..4b0f942b8914 100644
--- a/drivers/iio/dac/ad7303.c
+++ b/drivers/iio/dac/ad7303.c
@@ -184,9 +184,9 @@ static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
.address = (chan), \
.scan_type = { \
.sign = 'u', \
- .realbits = '8', \
- .storagebits = '8', \
- .shift = '0', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ .shift = 0, \
}, \
.ext_info = ad7303_ext_info, \
}
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index a0aedda7dfd7..bf0bd7e03aff 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -50,7 +50,6 @@
#define AS3935_TUNE_CAP 0x08
#define AS3935_CALIBRATE 0x3D
-#define AS3935_WRITE_DATA BIT(15)
#define AS3935_READ_DATA BIT(14)
#define AS3935_ADDRESS(x) ((x) << 8)
@@ -105,7 +104,7 @@ static int as3935_write(struct as3935_state *st,
{
u8 *buf = st->buf;
- buf[0] = (AS3935_WRITE_DATA | AS3935_ADDRESS(reg)) >> 8;
+ buf[0] = AS3935_ADDRESS(reg) >> 8;
buf[1] = val;
return spi_write(st->spi, buf, 2);
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 34b1adad07aa..6a8024d9d742 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -277,8 +277,8 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
fl6.saddr = src_in->sin6_addr;
fl6.flowi6_oif = addr->bound_dev_if;
- dst = ip6_route_output(addr->net, NULL, &fl6);
- if ((ret = dst->error))
+ ret = ipv6_stub->ipv6_dst_lookup(addr->net, NULL, &dst, &fl6);
+ if (ret < 0)
goto put;
if (ipv6_addr_any(&fl6.saddr)) {
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index b1f37d4095fa..e76d52a203a7 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -863,7 +863,7 @@ err_put:
free_port_list_attributes(device);
err_unregister:
- device_unregister(class_dev);
+ device_del(class_dev);
err:
return ret;
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 77ddf2fa8625..8763fb832b01 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -2491,6 +2491,7 @@ err_counter:
mlx4_ib_delete_counters_table(ibdev, &ibdev->counters_table[i]);
err_map:
+ mlx4_ib_free_eqs(dev, ibdev);
iounmap(ibdev->uar_map);
err_uar:
diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c
index 36ec8aa048aa..0b5bb0cee6f9 100644
--- a/drivers/infiniband/hw/mlx4/mcg.c
+++ b/drivers/infiniband/hw/mlx4/mcg.c
@@ -1105,7 +1105,8 @@ static void _mlx4_ib_mcg_port_cleanup(struct mlx4_ib_demux_ctx *ctx, int destroy
while ((p = rb_first(&ctx->mcg_table)) != NULL) {
group = rb_entry(p, struct mcast_group, node);
if (atomic_read(&group->refcount))
- mcg_warn_group(group, "group refcount %d!!! (pointer %p)\n", atomic_read(&group->refcount), group);
+ mcg_debug_group(group, "group refcount %d!!! (pointer %p)\n",
+ atomic_read(&group->refcount), group);
force_clean_group(group);
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
index 6bd5740e2691..09396bd7b02d 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
@@ -281,8 +281,11 @@ void ipoib_delete_debug_files(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
+ WARN_ONCE(!priv->mcg_dentry, "null mcg debug file\n");
+ WARN_ONCE(!priv->path_dentry, "null path debug file\n");
debugfs_remove(priv->mcg_dentry);
debugfs_remove(priv->path_dentry);
+ priv->mcg_dentry = priv->path_dentry = NULL;
}
int ipoib_register_debugfs(void)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 8efcff1beb8f..6699ecd855f0 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -106,6 +106,33 @@ static struct ib_client ipoib_client = {
.get_net_dev_by_params = ipoib_get_net_dev_by_params,
};
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+static int ipoib_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct netdev_notifier_info *ni = ptr;
+ struct net_device *dev = ni->dev;
+
+ if (dev->netdev_ops->ndo_open != ipoib_open)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_REGISTER:
+ ipoib_create_debug_files(dev);
+ break;
+ case NETDEV_CHANGENAME:
+ ipoib_delete_debug_files(dev);
+ ipoib_create_debug_files(dev);
+ break;
+ case NETDEV_UNREGISTER:
+ ipoib_delete_debug_files(dev);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+#endif
+
int ipoib_open(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -1595,8 +1622,6 @@ void ipoib_dev_cleanup(struct net_device *dev)
ASSERT_RTNL();
- ipoib_delete_debug_files(dev);
-
/* Delete any child interfaces first */
list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) {
/* Stop GC on child */
@@ -1908,8 +1933,6 @@ static struct net_device *ipoib_add_port(const char *format,
goto register_failed;
}
- ipoib_create_debug_files(priv->dev);
-
if (ipoib_cm_add_mode_attr(priv->dev))
goto sysfs_failed;
if (ipoib_add_pkey_attr(priv->dev))
@@ -1924,7 +1947,6 @@ static struct net_device *ipoib_add_port(const char *format,
return priv->dev;
sysfs_failed:
- ipoib_delete_debug_files(priv->dev);
unregister_netdev(priv->dev);
register_failed:
@@ -2006,6 +2028,12 @@ static void ipoib_remove_one(struct ib_device *device, void *client_data)
kfree(dev_list);
}
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+static struct notifier_block ipoib_netdev_notifier = {
+ .notifier_call = ipoib_netdev_event,
+};
+#endif
+
static int __init ipoib_init_module(void)
{
int ret;
@@ -2057,6 +2085,9 @@ static int __init ipoib_init_module(void)
if (ret)
goto err_client;
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+ register_netdevice_notifier(&ipoib_netdev_notifier);
+#endif
return 0;
err_client:
@@ -2074,6 +2105,9 @@ err_fs:
static void __exit ipoib_cleanup_module(void)
{
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+ unregister_netdevice_notifier(&ipoib_netdev_notifier);
+#endif
ipoib_netlink_fini();
ib_unregister_client(&ipoib_client);
ib_sa_unregister_client(&ipoib_sa_client);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
index fca1a882de27..57a34f87dedf 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
@@ -85,8 +85,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
goto register_failed;
}
- ipoib_create_debug_files(priv->dev);
-
/* RTNL childs don't need proprietary sysfs entries */
if (type == IPOIB_LEGACY_CHILD) {
if (ipoib_cm_add_mode_attr(priv->dev))
@@ -107,7 +105,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
sysfs_failed:
result = -ENOMEM;
- ipoib_delete_debug_files(priv->dev);
unregister_netdevice(priv->dev);
register_failed:
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 0628372f3591..b92b8a724efb 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2005,11 +2005,14 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
if (context_copied(context)) {
u16 did_old = context_domain_id(context);
- if (did_old >= 0 && did_old < cap_ndoms(iommu->cap))
+ if (did_old >= 0 && did_old < cap_ndoms(iommu->cap)) {
iommu->flush.flush_context(iommu, did_old,
(((u16)bus) << 8) | devfn,
DMA_CCMD_MASK_NOBIT,
DMA_CCMD_DEVICE_INVL);
+ iommu->flush.flush_iotlb(iommu, did_old, 0, 0,
+ DMA_TLB_DSI_FLUSH);
+ }
}
pgd = domain->pgd;
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 4c15dee0857b..27713412c881 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -374,6 +374,7 @@ config DM_LOG_USERSPACE
config DM_RAID
tristate "RAID 1/4/5/6/10 target"
depends on BLK_DEV_DM
+ select MD_RAID0
select MD_RAID1
select MD_RAID10
select MD_RAID456
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 2dd33085b331..cdceefd0e57d 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -222,7 +222,7 @@ static DEFINE_SPINLOCK(param_spinlock);
* Buffers are freed after this timeout
*/
static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS;
-static unsigned dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES;
+static unsigned long dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES;
static unsigned long dm_bufio_peak_allocated;
static unsigned long dm_bufio_allocated_kmem_cache;
@@ -914,10 +914,11 @@ static void __get_memory_limit(struct dm_bufio_client *c,
{
unsigned long buffers;
- if (ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch) {
- mutex_lock(&dm_bufio_clients_lock);
- __cache_size_refresh();
- mutex_unlock(&dm_bufio_clients_lock);
+ if (unlikely(ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch)) {
+ if (mutex_trylock(&dm_bufio_clients_lock)) {
+ __cache_size_refresh();
+ mutex_unlock(&dm_bufio_clients_lock);
+ }
}
buffers = dm_bufio_cache_size_per_client >>
@@ -1513,10 +1514,10 @@ static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp)
return true;
}
-static unsigned get_retain_buffers(struct dm_bufio_client *c)
+static unsigned long get_retain_buffers(struct dm_bufio_client *c)
{
- unsigned retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes);
- return retain_bytes / c->block_size;
+ unsigned long retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes);
+ return retain_bytes >> (c->sectors_per_block_bits + SECTOR_SHIFT);
}
static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
@@ -1526,7 +1527,7 @@ static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
struct dm_buffer *b, *tmp;
unsigned long freed = 0;
unsigned long count = nr_to_scan;
- unsigned retain_target = get_retain_buffers(c);
+ unsigned long retain_target = get_retain_buffers(c);
for (l = 0; l < LIST_SIZE; l++) {
list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) {
@@ -1752,11 +1753,19 @@ static bool older_than(struct dm_buffer *b, unsigned long age_hz)
static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz)
{
struct dm_buffer *b, *tmp;
- unsigned retain_target = get_retain_buffers(c);
- unsigned count;
+ unsigned long retain_target = get_retain_buffers(c);
+ unsigned long count;
+ LIST_HEAD(write_list);
dm_bufio_lock(c);
+ __check_watermark(c, &write_list);
+ if (unlikely(!list_empty(&write_list))) {
+ dm_bufio_unlock(c);
+ __flush_write_list(&write_list);
+ dm_bufio_lock(c);
+ }
+
count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_CLEAN], lru_list) {
if (count <= retain_target)
@@ -1781,6 +1790,8 @@ static void cleanup_old_buffers(void)
mutex_lock(&dm_bufio_clients_lock);
+ __cache_size_refresh();
+
list_for_each_entry(c, &dm_bufio_all_clients, client_list)
__evict_old_buffers(c, max_age_hz);
@@ -1904,7 +1915,7 @@ MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache");
module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds");
-module_param_named(retain_bytes, dm_bufio_retain_bytes, uint, S_IRUGO | S_IWUSR);
+module_param_named(retain_bytes, dm_bufio_retain_bytes, ulong, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(retain_bytes, "Try to keep at least this many bytes cached in memory");
module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR);
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index 3970cda10080..d3c55d7754af 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -1326,17 +1326,19 @@ void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd,
int dm_cache_commit(struct dm_cache_metadata *cmd, bool clean_shutdown)
{
- int r;
+ int r = -EINVAL;
flags_mutator mutator = (clean_shutdown ? set_clean_shutdown :
clear_clean_shutdown);
WRITE_LOCK(cmd);
+ if (cmd->fail_io)
+ goto out;
+
r = __commit_transaction(cmd, mutator);
if (r)
goto out;
r = __begin_transaction(cmd);
-
out:
WRITE_UNLOCK(cmd);
return r;
@@ -1348,7 +1350,8 @@ int dm_cache_get_free_metadata_block_count(struct dm_cache_metadata *cmd,
int r = -EINVAL;
READ_LOCK(cmd);
- r = dm_sm_get_nr_free(cmd->metadata_sm, result);
+ if (!cmd->fail_io)
+ r = dm_sm_get_nr_free(cmd->metadata_sm, result);
READ_UNLOCK(cmd);
return r;
@@ -1360,7 +1363,8 @@ int dm_cache_get_metadata_dev_size(struct dm_cache_metadata *cmd,
int r = -EINVAL;
READ_LOCK(cmd);
- r = dm_sm_get_nr_blocks(cmd->metadata_sm, result);
+ if (!cmd->fail_io)
+ r = dm_sm_get_nr_blocks(cmd->metadata_sm, result);
READ_UNLOCK(cmd);
return r;
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
index 665bf3285618..32e76c5ee741 100644
--- a/drivers/md/dm-era-target.c
+++ b/drivers/md/dm-era-target.c
@@ -961,15 +961,15 @@ static int metadata_commit(struct era_metadata *md)
}
}
- r = save_sm_root(md);
+ r = dm_tm_pre_commit(md->tm);
if (r) {
- DMERR("%s: save_sm_root failed", __func__);
+ DMERR("%s: pre commit failed", __func__);
return r;
}
- r = dm_tm_pre_commit(md->tm);
+ r = save_sm_root(md);
if (r) {
- DMERR("%s: pre commit failed", __func__);
+ DMERR("%s: save_sm_root failed", __func__);
return r;
}
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 911ada643364..3b67afda430b 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -485,11 +485,11 @@ static int __write_initial_superblock(struct dm_pool_metadata *pmd)
if (r < 0)
return r;
- r = save_sm_roots(pmd);
+ r = dm_tm_pre_commit(pmd->tm);
if (r < 0)
return r;
- r = dm_tm_pre_commit(pmd->tm);
+ r = save_sm_roots(pmd);
if (r < 0)
return r;
diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c
index 88487346c4c6..727aacbb1480 100644
--- a/drivers/md/dm-verity-avb.c
+++ b/drivers/md/dm-verity-avb.c
@@ -12,11 +12,14 @@
#define DM_MSG_PREFIX "verity-avb"
-/* Set via module parameter. */
+/* Set via module parameters. */
static char avb_vbmeta_device[64];
+static char avb_invalidate_on_error[4];
static void invalidate_vbmeta_endio(struct bio *bio)
{
+ if (bio->bi_error)
+ DMERR("invalidate_vbmeta_endio: error %d", bio->bi_error);
complete(bio->bi_private);
}
@@ -30,20 +33,19 @@ static int invalidate_vbmeta_submit(struct bio *bio,
bio->bi_private = &wait;
bio->bi_end_io = invalidate_vbmeta_endio;
bio->bi_bdev = bdev;
+ bio->bi_rw = rw;
bio->bi_iter.bi_sector = 0;
if (access_last_sector) {
- sector_t last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1;
+ sector_t last_sector;
+
+ 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;
+ if (!bio_add_page(bio, page, PAGE_SIZE, 0)) {
+ DMERR("invalidate_vbmeta_submit: bio_add_page error");
+ return -EIO;
+ }
submit_bio(rw, bio);
/* Wait up to 2 seconds for completion or fail. */
@@ -65,6 +67,9 @@ static int invalidate_vbmeta(dev_t vbmeta_devt)
int rw = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE;
int access_last_sector = 0;
+ DMINFO("invalidate_vbmeta: acting on device %d:%d",
+ MAJOR(vbmeta_devt), MINOR(vbmeta_devt));
+
/* First we open the device for reading. */
dev_mode = FMODE_READ | FMODE_EXCL;
bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode,
@@ -115,7 +120,7 @@ static int invalidate_vbmeta(dev_t vbmeta_devt)
goto failed_to_submit_read;
}
if (memcmp("AVBf", page_address(page) + offset, 4) != 0) {
- DMERR("invalidate_vbmeta called on non-vbmeta partition");
+ DMERR("invalidate_vbmeta on non-vbmeta partition");
ret = -EINVAL;
goto invalid_header;
}
@@ -175,6 +180,11 @@ void dm_verity_avb_error_handler(void)
DMINFO("AVB error handler called for %s", avb_vbmeta_device);
+ if (strcmp(avb_invalidate_on_error, "yes") != 0) {
+ DMINFO("Not configured to invalidate");
+ return;
+ }
+
if (avb_vbmeta_device[0] == '\0') {
DMERR("avb_vbmeta_device parameter not set");
goto fail_no_dev;
@@ -215,3 +225,5 @@ MODULE_LICENSE("GPL");
#undef MODULE_PARAM_PREFIX
#define MODULE_PARAM_PREFIX "androidboot.vbmeta."
module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0);
+module_param_string(invalidate_on_error, avb_invalidate_on_error,
+ sizeof(avb_invalidate_on_error), 0);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 5d42d8f09421..1a7b11d57256 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2191,8 +2191,11 @@ static void dm_request_fn(struct request_queue *q)
tio = tio_from_request(rq);
/* Establish tio->ti before queuing work (map_tio_request) */
tio->ti = ti;
- queue_kthread_work(&md->kworker, &tio->work);
+ spin_unlock(q->queue_lock);
+ if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE)
+ dm_requeue_original_request(md, rq);
BUG_ON(!irqs_disabled());
+ spin_lock(q->queue_lock);
}
goto out;
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
index b1ced58eb5e1..a1a68209bd36 100644
--- a/drivers/md/persistent-data/dm-btree.c
+++ b/drivers/md/persistent-data/dm-btree.c
@@ -887,8 +887,12 @@ static int find_key(struct ro_spine *s, dm_block_t block, bool find_highest,
else
*result_key = le64_to_cpu(ro_node(s)->keys[0]);
- if (next_block || flags & INTERNAL_NODE)
- block = value64(ro_node(s), i);
+ if (next_block || flags & INTERNAL_NODE) {
+ if (find_highest)
+ block = value64(ro_node(s), i);
+ else
+ block = value64(ro_node(s), 0);
+ }
} while (flags & INTERNAL_NODE);
diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c
index ebb280a14325..32adf6b4a9c7 100644
--- a/drivers/md/persistent-data/dm-space-map-disk.c
+++ b/drivers/md/persistent-data/dm-space-map-disk.c
@@ -142,10 +142,23 @@ static int sm_disk_inc_block(struct dm_space_map *sm, dm_block_t b)
static int sm_disk_dec_block(struct dm_space_map *sm, dm_block_t b)
{
+ int r;
+ uint32_t old_count;
enum allocation_event ev;
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
- return sm_ll_dec(&smd->ll, b, &ev);
+ r = sm_ll_dec(&smd->ll, b, &ev);
+ if (!r && (ev == SM_FREE)) {
+ /*
+ * It's only free if it's also free in the last
+ * transaction.
+ */
+ r = sm_ll_lookup(&smd->old_ll, b, &old_count);
+ if (!r && !old_count)
+ smd->nr_allocated_this_transaction--;
+ }
+
+ return r;
}
static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 7af976934441..4384b46cee1a 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2232,6 +2232,10 @@ static int resize_stripes(struct r5conf *conf, int newsize)
err = -ENOMEM;
mutex_unlock(&conf->cache_size_mutex);
+
+ conf->slab_cache = sc;
+ conf->active_name = 1-conf->active_name;
+
/* Step 4, return new stripes to service */
while(!list_empty(&newstripes)) {
nsh = list_entry(newstripes.next, struct stripe_head, lru);
@@ -2249,8 +2253,6 @@ static int resize_stripes(struct r5conf *conf, int newsize)
}
/* critical section pass, GFP_NOIO no longer needed */
- conf->slab_cache = sc;
- conf->active_name = 1-conf->active_name;
if (!err)
conf->pool_size = newsize;
return err;
diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c
index fdffb2f0ded8..107853b0fddd 100644
--- a/drivers/media/dvb-frontends/cxd2841er.c
+++ b/drivers/media/dvb-frontends/cxd2841er.c
@@ -2678,7 +2678,9 @@ static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = {
FE_CAN_MUTE_TS |
FE_CAN_2G_MODULATION,
.frequency_min = 42000000,
- .frequency_max = 1002000000
+ .frequency_max = 1002000000,
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000
},
.init = cxd2841er_init_tc,
.sleep = cxd2841er_sleep_tc,
diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c
index 359a860fdabb..b382a1d83d92 100644
--- a/drivers/media/i2c/adv7481.c
+++ b/drivers/media/i2c/adv7481.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, 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
@@ -27,29 +27,68 @@
#include <media/v4l2-ctrls.h>
#include <linux/mutex.h>
#include <linux/delay.h>
+
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
#include <media/adv7481.h>
#include <media/msm_ba.h>
#include "adv7481_reg.h"
+#include "msm_cci.h"
+#include "msm_camera_i2c.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
+
#define DRIVER_NAME "adv7481"
-#define I2C_RW_DELAY 100
-#define I2C_SW_RST_DELAY 10000
-#define GPIO_HW_DELAY_LOW 100000
-#define GPIO_HW_DELAY_HI 10000
+#define I2C_RW_DELAY 1
+#define I2C_SW_RST_DELAY 5000
+#define GPIO_HW_RST_DELAY_HI 10000
+#define GPIO_HW_RST_DELAY_LOW 10000
#define SDP_MIN_SLEEP 5000
#define SDP_MAX_SLEEP 6000
#define SDP_NUM_TRIES 30
#define LOCK_MIN_SLEEP 5000
#define LOCK_MAX_SLEEP 6000
-#define LOCK_NUM_TRIES 20
+#define LOCK_NUM_TRIES 200
+
+#define MAX_DEFAULT_WIDTH 1280
+#define MAX_DEFAULT_HEIGHT 720
+#define MAX_DEFAULT_FRAME_RATE 60
+#define MAX_DEFAULT_PIX_CLK_HZ 74240000
#define ONE_MHZ_TO_HZ 1000000
+#define I2C_BLOCK_WRITE_SIZE 1024
+
+enum adv7481_gpio_t {
+
+ CCI_I2C_SDA = 0,
+ CCI_I2C_SCL,
+
+ ADV7481_GPIO_RST,
+
+ ADV7481_GPIO_INT1,
+ ADV7481_GPIO_INT2,
+ ADV7481_GPIO_INT3,
+
+ ADV7481_GPIO_MAX,
+};
struct adv7481_state {
- /* Platform Data */
- struct adv7481_platform_data pdata;
+ struct device *dev;
+
+ /* VREG */
+ struct camera_vreg_t *cci_vreg;
+ struct regulator *cci_reg_ptr[MAX_REGULATOR];
+ int32_t regulator_count;
+
+ /* I2C */
+ struct msm_camera_i2c_client i2c_client;
+ u32 cci_master;
+ u32 i2c_slave_addr;
/* V4L2 Data */
struct v4l2_subdev sd;
@@ -63,19 +102,25 @@ struct adv7481_state {
struct workqueue_struct *work_queues;
struct mutex mutex;
- struct i2c_client *client;
- struct i2c_client *i2c_csi_txa;
- struct i2c_client *i2c_csi_txb;
- struct i2c_client *i2c_hdmi;
- struct i2c_client *i2c_edid;
- struct i2c_client *i2c_cp;
- struct i2c_client *i2c_sdp;
- struct i2c_client *i2c_rep;
+ uint8_t i2c_io_addr;
+ uint8_t i2c_csi_txa_addr;
+ uint8_t i2c_csi_txb_addr;
+ uint8_t i2c_hdmi_addr;
+ uint8_t i2c_edid_addr;
+ uint8_t i2c_cp_addr;
+ uint8_t i2c_sdp_addr;
+ uint8_t i2c_rep_addr;
+ uint8_t i2c_cbus_addr;
/* device status and Flags */
int irq;
int device_num;
int powerup;
+ int cec_detected;
+ int clocks_requested;
+
+ /* GPIOs */
+ struct gpio gpio_array[ADV7481_GPIO_MAX];
/* routing configuration data */
int csia_src;
@@ -196,6 +241,9 @@ const uint8_t adv7481_default_edid_data[] = {
#define ADV7481_EDID_SIZE ARRAY_SIZE(adv7481_default_edid_data)
+static u32 adv7481_inp_to_ba(u32 adv_input);
+static bool adv7481_is_timing_locked(struct adv7481_state *state);
+
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &(container_of(ctrl->handler,
@@ -208,56 +256,151 @@ static inline struct adv7481_state *to_state(struct v4l2_subdev *sd)
}
/* I2C Rd/Rw Functions */
-static int adv7481_wr_byte(struct i2c_client *i2c_client, unsigned int reg,
- unsigned int value)
+static int32_t adv7481_cci_i2c_write(struct msm_camera_i2c_client *i2c_client,
+ uint8_t reg, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
{
- int ret;
+ return i2c_client->i2c_func_tbl->i2c_write(i2c_client, reg,
+ *data, data_type);
+}
- ret = i2c_smbus_write_byte_data(i2c_client, reg & 0xFF, value);
- usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY);
+static int32_t adv7481_cci_i2c_write_seq(
+ struct msm_camera_i2c_client *i2c_client,
+ uint8_t reg, const uint8_t *data, uint32_t size)
+{
+ return i2c_client->i2c_func_tbl->i2c_write_seq(i2c_client, reg,
+ (uint8_t *)data, size);
+}
+
+static int32_t adv7481_cci_i2c_read(struct msm_camera_i2c_client *i2c_client,
+ uint8_t reg, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ return i2c_client->i2c_func_tbl->i2c_read(i2c_client, reg,
+ data, data_type);
+}
+
+static int32_t adv7481_wr_byte(struct msm_camera_i2c_client *c_i2c_client,
+ uint8_t sid, uint8_t reg, uint8_t data)
+{
+ uint16_t write_data = data;
+ int ret = 0;
+
+ c_i2c_client->cci_client->sid = sid;
+
+ ret = adv7481_cci_i2c_write(c_i2c_client, reg, &write_data,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ if (ret < 0)
+ pr_err("Error %d writing cci i2c\n", ret);
return ret;
}
-static int adv7481_rd_byte(struct i2c_client *i2c_client, unsigned int reg)
+static int32_t adv7481_wr_block(struct msm_camera_i2c_client *c_i2c_client,
+ uint8_t sid, uint8_t reg, const uint8_t *data, uint32_t size)
{
- int ret;
+ int ret = 0;
- ret = i2c_smbus_read_byte_data(i2c_client, reg & 0xFF);
- usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY);
+ c_i2c_client->cci_client->sid = sid;
+
+ ret = adv7481_cci_i2c_write_seq(c_i2c_client, reg, data, size);
+ if (ret < 0)
+ pr_err("Error %d writing cci i2c block data\n", ret);
return ret;
}
+static uint8_t adv7481_rd_byte(struct msm_camera_i2c_client *c_i2c_client,
+ uint8_t sid, uint8_t reg)
+{
+ uint16_t data = 0;
+ int ret = 0;
+
+ c_i2c_client->cci_client->sid = sid;
+ ret = adv7481_cci_i2c_read(c_i2c_client, reg, &data,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ if (ret < 0) {
+ pr_err("Error %d reading cci i2c\n", ret);
+ return ret;
+ }
+
+ return (uint8_t)(data & 0xFF);
+}
+
+static uint16_t adv7481_rd_word(struct msm_camera_i2c_client *c_i2c_client,
+ uint8_t sid, uint8_t reg)
+{
+ uint16_t data = 0;
+ int ret;
+
+ c_i2c_client->cci_client->sid = sid;
+ ret = adv7481_cci_i2c_read(c_i2c_client, reg, &data,
+ MSM_CAMERA_I2C_WORD_DATA);
+ if (ret < 0) {
+ pr_err("Error %d reading cci i2c\n", ret);
+ return ret;
+ }
+
+ return data;
+}
+
static int adv7481_set_irq(struct adv7481_state *state)
{
int ret = 0;
- ret = adv7481_wr_byte(state->client, IO_REG_PAD_CTRL_1_ADDR,
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_PAD_CTRL_1_ADDR,
ADV_REG_SETFIELD(1, IO_PDN_INT2) |
ADV_REG_SETFIELD(1, IO_PDN_INT3) |
ADV_REG_SETFIELD(1, IO_INV_LLC) |
ADV_REG_SETFIELD(AD_MID_DRIVE_STRNGTH, IO_DRV_LLC_PAD));
- ret |= adv7481_wr_byte(state->client, IO_REG_INT1_CONF_ADDR,
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_INT1_CONF_ADDR,
ADV_REG_SETFIELD(AD_ACTIVE_UNTIL_CLR,
IO_INTRQ_DUR_SEL) |
ADV_REG_SETFIELD(AD_OP_DRIVE_LOW, IO_INTRQ_OP_SEL));
- ret |= adv7481_wr_byte(state->client, IO_REG_INT2_CONF_ADDR,
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_INT2_CONF_ADDR,
ADV_REG_SETFIELD(1, IO_CP_LOCK_UNLOCK_EDGE_SEL));
- ret |= adv7481_wr_byte(state->client, IO_REG_DATAPATH_INT_MASKB_ADDR,
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_DATAPATH_INT_MASKB_ADDR,
ADV_REG_SETFIELD(1, IO_CP_LOCK_CP_MB1) |
ADV_REG_SETFIELD(1, IO_CP_UNLOCK_CP_MB1) |
ADV_REG_SETFIELD(1, IO_VMUTE_REQUEST_HDMI_MB1) |
ADV_REG_SETFIELD(1, IO_INT_SD_MB1));
- /* Set hpa */
- ret |= adv7481_wr_byte(state->client, IO_HDMI_LVL_INT_MASKB_3_ADDR,
- ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1));
+ /* Set cable detect */
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_HDMI_LVL_INT_MASKB_3_ADDR,
+ ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1) |
+ ADV_REG_SETFIELD(1, IO_V_LOCKED_MB1) |
+ ADV_REG_SETFIELD(1, IO_DE_REGEN_LCK_MB1));
+
+ /* set CVBS lock/unlock interrupts */
+ /* Select SDP MAP 1 */
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x20);
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_LOCK_UNLOCK_MASK_ADDR, 0x03);
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x00);
if (ret)
pr_err("%s: Failed %d to setup interrupt regs\n",
__func__, ret);
- else
- enable_irq(state->irq);
+
+ return ret;
+}
+
+static int adv7481_reset_irq(struct adv7481_state *state)
+{
+ int ret = 0;
+
+ disable_irq(state->irq);
+
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_DATAPATH_INT_MASKB_ADDR, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_HDMI_LVL_INT_MASKB_3_ADDR, 0x00);
return ret;
}
@@ -267,27 +410,42 @@ static int adv7481_set_edid(struct adv7481_state *state)
int i;
int ret = 0;
uint8_t edid_state;
+ uint32_t data_left = 0;
+ uint32_t start_pos;
/* Enable Manual Control of EDID on Port A */
- ret |= adv7481_wr_byte(state->i2c_rep, 0x74, 0x01);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x74,
+ 0x01);
/* Disable Auto Enable of EDID */
- ret |= adv7481_wr_byte(state->i2c_rep, 0x7A, 0x08);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x7A,
+ 0x08);
/* Set Primary EDID Size to 256 Bytes */
- ret |= adv7481_wr_byte(state->i2c_rep, 0x70, 0x20);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x70,
+ 0x20);
/*
* Readback EDID enable state after a combination of manual
* and automatic functions
*/
- edid_state = adv7481_rd_byte(state->i2c_rep,
+ edid_state = adv7481_rd_byte(&state->i2c_client, state->i2c_rep_addr,
HDMI_REG_RO_EDID_DEBUG_2_ADDR);
pr_debug("%s: Readback EDID enable state: 0x%x\n", __func__,
edid_state);
- for (i = 0; i < ADV7481_EDID_SIZE; i++) {
- ret |= adv7481_wr_byte(state->i2c_edid, i,
- adv7481_default_edid_data[i]);
- }
+ for (i = 0; i < ADV7481_EDID_SIZE && !ret; i += I2C_BLOCK_WRITE_SIZE)
+ ret |= adv7481_wr_block(&state->i2c_client,
+ state->i2c_edid_addr,
+ i, &adv7481_default_edid_data[i],
+ I2C_BLOCK_WRITE_SIZE);
+
+ data_left = ADV7481_EDID_SIZE % I2C_BLOCK_WRITE_SIZE;
+ start_pos = ADV7481_EDID_SIZE - data_left;
+ if (data_left && !ret)
+ ret |= adv7481_wr_block(&state->i2c_client,
+ state->i2c_edid_addr,
+ start_pos,
+ &adv7481_default_edid_data[start_pos],
+ data_left);
return ret;
}
@@ -301,47 +459,232 @@ static irqreturn_t adv7481_irq(int irq, void *dev)
return IRQ_HANDLED;
}
+/* Request CCI clocks for adv7481 register access */
+static int adv7481_request_cci_clks(struct adv7481_state *state)
+{
+ int ret = 0;
+
+ if (state->clocks_requested == TRUE)
+ return ret;
+
+ ret = state->i2c_client.i2c_func_tbl->i2c_util(
+ &state->i2c_client, MSM_CCI_INIT);
+ if (ret < 0)
+ pr_err("%s - cci_init failed\n", __func__);
+ else
+ state->clocks_requested = TRUE;
+
+ /* enable camera voltage regulator */
+ ret = msm_camera_enable_vreg(state->dev, state->cci_vreg,
+ state->regulator_count, NULL, 0,
+ &state->cci_reg_ptr[0], 1);
+ if (ret < 0)
+ pr_err("%s:cci enable_vreg failed\n", __func__);
+ else
+ pr_debug("%s - VREG Initialized...\n", __func__);
+
+ return ret;
+}
+
+static int adv7481_release_cci_clks(struct adv7481_state *state)
+{
+ int ret = 0;
+
+ if (state->clocks_requested == FALSE)
+ return ret;
+
+ ret = state->i2c_client.i2c_func_tbl->i2c_util(
+ &state->i2c_client, MSM_CCI_RELEASE);
+ if (ret < 0)
+ pr_err("%s - cci_release failed\n", __func__);
+ else
+ state->clocks_requested = FALSE;
+
+ /* disable camera voltage regulator */
+ ret = msm_camera_enable_vreg(state->dev, state->cci_vreg,
+ state->regulator_count, NULL, 0,
+ &state->cci_reg_ptr[0], 0);
+ if (ret < 0)
+ pr_err("%s:cci disable vreg failed\n", __func__);
+ else
+ pr_debug("%s - VREG Initialized...\n", __func__);
+
+ return ret;
+}
+
static void adv7481_irq_delay_work(struct work_struct *work)
{
struct adv7481_state *state;
- uint8_t status;
+ uint8_t int_raw_status;
+ uint8_t int_status;
+ uint8_t raw_status;
state = container_of(work, struct adv7481_state,
irq_delayed_work.work);
mutex_lock(&state->mutex);
- /* workaround for irq trigger */
- status = adv7481_rd_byte(state->client,
+ /* Read raw irq status register */
+ int_raw_status = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
IO_REG_INT_RAW_STATUS_ADDR);
pr_debug("%s: dev: %d got int raw status: 0x%x\n", __func__,
- state->device_num, status);
+ state->device_num, int_raw_status);
+ state->cec_detected = ADV_REG_GETFIELD(int_raw_status, IO_INT_CEC_ST);
+
+ while (int_raw_status) {
+ if (ADV_REG_GETFIELD(int_raw_status, IO_INTRQ1_RAW)) {
+ int lock_status = -1;
+ struct v4l2_event event = {0};
+ int *ptr = (int *)event.u.data;
+
+ pr_debug("%s: dev: %d got intrq1_raw\n", __func__,
+ state->device_num);
+ int_status = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_io_addr,
+ IO_REG_DATAPATH_INT_STATUS_ADDR);
+
+ raw_status = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_io_addr,
+ IO_REG_DATAPATH_RAW_STATUS_ADDR);
+
+ adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_DATAPATH_INT_CLEAR_ADDR, int_status);
+
+ pr_debug("%s: dev: %d got datapath int status: 0x%x\n",
+ __func__, state->device_num, int_status);
+
+ pr_debug("%s: dev: %d got datapath raw status: 0x%x\n",
+ __func__, state->device_num, raw_status);
+
+ if (ADV_REG_GETFIELD(int_status, IO_INT_SD_ST) &&
+ ADV_REG_GETFIELD(raw_status, IO_INT_SD_RAW)) {
+ uint8_t sdp_sts = 0;
+
+ adv7481_wr_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RW_MAP_REG,
+ 0x01);
+ sdp_sts = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_sdp_addr,
+ SDP_RO_MAIN_STATUS1_ADDR);
+ pr_debug("%s: dev: %d got sdp status: 0x%x\n",
+ __func__, state->device_num, sdp_sts);
+ adv7481_wr_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RW_MAP_REG,
+ 0x00);
+ if (ADV_REG_GETFIELD(sdp_sts,
+ SDP_RO_MAIN_IN_LOCK)) {
+ lock_status = 0;
+ pr_debug(
+ "%s: set lock_status SDP_IN_LOCK:0x%x\n",
+ __func__, lock_status);
+ } else {
+ lock_status = 1;
+ pr_debug(
+ "%s: set lock_status SDP_UNLOCK:0x%x\n",
+ __func__, lock_status);
+ }
+ adv7481_wr_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RW_MAP_REG,
+ 0x20);
+ adv7481_wr_byte(&state->i2c_client,
+ state->i2c_sdp_addr,
+ SDP_RW_LOCK_UNLOCK_CLR_ADDR, sdp_sts);
+ adv7481_wr_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RW_MAP_REG,
+ 0x00);
+ } else {
+ if (ADV_REG_GETFIELD(int_status,
+ IO_CP_LOCK_CP_ST) &&
+ ADV_REG_GETFIELD(raw_status,
+ IO_CP_LOCK_CP_RAW)) {
+ lock_status = 0;
+ pr_debug(
+ "%s: set lock_status IO_CP_LOCK_CP_RAW:0x%x\n",
+ __func__, lock_status);
+ }
+ if (ADV_REG_GETFIELD(int_status,
+ IO_CP_UNLOCK_CP_ST) &&
+ ADV_REG_GETFIELD(raw_status,
+ IO_CP_UNLOCK_CP_RAW)) {
+ lock_status = 1;
+ pr_debug(
+ "%s: set lock_status IO_CP_UNLOCK_CP_RAW:0x%x\n",
+ __func__, lock_status);
+ }
+ }
+
+ if (lock_status >= 0) {
+ ptr[0] = adv7481_inp_to_ba(state->mode);
+ ptr[1] = lock_status;
+ event.type = lock_status ?
+ V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK :
+ V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK;
+ v4l2_subdev_notify(&state->sd,
+ event.type, &event);
+ }
+ }
- status = adv7481_rd_byte(state->client,
- IO_REG_DATAPATH_INT_STATUS_ADDR);
+ if (ADV_REG_GETFIELD(int_raw_status, IO_INT_HDMI_ST)) {
+ int cable_detected = 0;
+ struct v4l2_event event = {0};
+ int *ptr = (int *)event.u.data;
- pr_debug("%s: dev: %d got datapath int status: 0x%x\n", __func__,
- state->device_num, status);
+ ptr[0] = adv7481_inp_to_ba(state->mode);
- adv7481_wr_byte(state->client,
- IO_REG_DATAPATH_INT_CLEAR_ADDR, status);
+ pr_debug("%s: dev: %d got int_hdmi_st\n", __func__,
+ state->device_num);
- status = adv7481_rd_byte(state->client,
- IO_REG_DATAPATH_RAW_STATUS_ADDR);
+ int_status = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_io_addr,
+ IO_HDMI_LVL_INT_STATUS_3_ADDR);
- pr_debug("%s: dev: %d got datapath rawstatus: 0x%x\n", __func__,
- state->device_num, status);
+ raw_status = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_io_addr,
+ IO_HDMI_LVL_RAW_STATUS_3_ADDR);
- status = adv7481_rd_byte(state->client,
- IO_HDMI_LVL_INT_STATUS_3_ADDR);
+ adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_HDMI_LVL_INT_CLEAR_3_ADDR, int_status);
- pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n", __func__,
- state->device_num, status);
+ pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n",
+ __func__, state->device_num, int_status);
+ pr_debug("%s: dev: %d got hdmi lvl raw status 3: 0x%x\n",
+ __func__, state->device_num, raw_status);
- adv7481_wr_byte(state->client,
- IO_HDMI_LVL_INT_CLEAR_3_ADDR, status);
+ if (ADV_REG_GETFIELD(int_status, IO_CABLE_DET_A_ST)) {
+ cable_detected = ADV_REG_GETFIELD(raw_status,
+ IO_CABLE_DET_A_RAW);
+ pr_debug("%s: set cable_detected: 0x%x\n",
+ __func__, cable_detected);
+ ptr[1] = cable_detected;
+ event.type = V4L2_EVENT_MSM_BA_CABLE_DETECT;
+ v4l2_subdev_notify(&state->sd,
+ event.type, &event);
+ }
+ /* Assumption is that vertical sync int
+ * is the last one to come
+ */
+ if (ADV_REG_GETFIELD(int_status, IO_V_LOCKED_ST)) {
+ if (ADV_REG_GETFIELD(raw_status,
+ IO_TMDSPLL_LCK_A_RAW) &&
+ ADV_REG_GETFIELD(raw_status,
+ IO_V_LOCKED_RAW) &&
+ ADV_REG_GETFIELD(raw_status,
+ IO_DE_REGEN_LCK_RAW)) {
+ pr_debug("%s: port settings changed\n",
+ __func__);
+ event.type =
+ V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED;
+ v4l2_subdev_notify(&state->sd,
+ event.type, &event);
+ }
+ }
+ }
+ int_raw_status = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_io_addr,
+ IO_REG_INT_RAW_STATUS_ADDR);
+ }
mutex_unlock(&state->mutex);
}
@@ -350,110 +693,100 @@ static int adv7481_cec_wakeup(struct adv7481_state *state, bool enable)
uint8_t val;
int ret = 0;
- val = adv7481_rd_byte(state->client,
+ val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
IO_REG_PWR_DN2_XTAL_HIGH_ADDR);
val = ADV_REG_GETFIELD(val, IO_PROG_XTAL_FREQ_HIGH);
if (enable) {
/* CEC wake up enabled in power-down mode */
val |= ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDN2B) |
ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDNB);
- ret = adv7481_wr_byte(state->client,
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val);
} else {
/* CEC wake up disabled in power-down mode */
val |= ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDN2B) |
ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDNB);
- ret = adv7481_wr_byte(state->client,
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val);
}
return ret;
}
/* Initialize adv7481 I2C Settings */
-static int adv7481_dev_init(struct adv7481_state *state,
- struct i2c_client *client)
+static int adv7481_dev_init(struct adv7481_state *state)
{
+ uint16_t chip_rev_id;
int ret;
mutex_lock(&state->mutex);
/* Soft reset */
- ret = adv7481_wr_byte(state->client,
- IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE);
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE);
/* Delay required following I2C reset and I2C transactions */
- usleep_range(I2C_SW_RST_DELAY, I2C_SW_RST_DELAY+1000);
+ udelay(I2C_SW_RST_DELAY);
+
+ chip_rev_id = adv7481_rd_word(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CHIP_REV_ID_1_ADDR);
+ pr_debug("%s: ADV7481 chip rev id: 0x%x", __func__, chip_rev_id);
/* Disable CEC wake up in power-down mode */
ret |= adv7481_cec_wakeup(state, 0);
/* Setting Vid_Std to 720x480p60 */
- ret |= adv7481_wr_byte(state->client,
- IO_REG_CP_VID_STD_ADDR, 0x4A);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CP_VID_STD_ADDR, 0x4A);
/* Configure I2C Maps and I2C Communication Settings */
/* io_reg_f2 I2C Auto Increment */
- ret |= adv7481_wr_byte(state->client, IO_REG_I2C_CFG_ADDR,
- IO_REG_I2C_AUTOINC_EN_REG_VALUE);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_I2C_CFG_ADDR, IO_REG_I2C_AUTOINC_EN_REG_VALUE);
/* DPLL Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_DPLL_ADDR,
- IO_REG_DPLL_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_DPLL_ADDR, IO_REG_DPLL_SADDR);
/* CP Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_CP_ADDR,
- IO_REG_CP_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CP_ADDR, IO_REG_CP_SADDR);
/* HDMI RX Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_ADDR,
- IO_REG_HDMI_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_HDMI_ADDR, IO_REG_HDMI_SADDR);
/* EDID Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_EDID_ADDR,
- IO_REG_EDID_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_EDID_ADDR, IO_REG_EDID_SADDR);
/* HDMI RX Repeater Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_REP_ADDR,
- IO_REG_HDMI_REP_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_HDMI_REP_ADDR, IO_REG_HDMI_REP_SADDR);
/* HDMI RX Info-frame Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_INF_ADDR,
- IO_REG_HDMI_INF_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_HDMI_INF_ADDR, IO_REG_HDMI_INF_SADDR);
/* CBUS Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_CBUS_ADDR,
- IO_REG_CBUS_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CBUS_ADDR, IO_REG_CBUS_SADDR);
/* CEC Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_CEC_ADDR,
- IO_REG_CEC_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CEC_ADDR, IO_REG_CEC_SADDR);
/* SDP Main Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_SDP_ADDR,
- IO_REG_SDP_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_SDP_ADDR, IO_REG_SDP_SADDR);
/* CSI-TXB Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXB_ADDR,
- IO_REG_CSI_TXB_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CSI_TXB_ADDR, IO_REG_CSI_TXB_SADDR);
/* CSI-TXA Map Address */
- ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXA_ADDR,
- IO_REG_CSI_TXA_SADDR);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CSI_TXA_ADDR, IO_REG_CSI_TXA_SADDR);
if (ret) {
pr_err("%s: Failed dev init %d\n", __func__, ret);
goto err_exit;
}
/* Configure i2c clients */
- state->i2c_csi_txa = i2c_new_dummy(client->adapter,
- IO_REG_CSI_TXA_SADDR >> 1);
- state->i2c_csi_txb = i2c_new_dummy(client->adapter,
- IO_REG_CSI_TXB_SADDR >> 1);
- state->i2c_cp = i2c_new_dummy(client->adapter,
- IO_REG_CP_SADDR >> 1);
- state->i2c_hdmi = i2c_new_dummy(client->adapter,
- IO_REG_HDMI_SADDR >> 1);
- state->i2c_edid = i2c_new_dummy(client->adapter,
- IO_REG_EDID_SADDR >> 1);
- state->i2c_sdp = i2c_new_dummy(client->adapter,
- IO_REG_SDP_SADDR >> 1);
- state->i2c_rep = i2c_new_dummy(client->adapter,
- IO_REG_HDMI_REP_SADDR >> 1);
-
- if (!state->i2c_csi_txa || !state->i2c_csi_txb || !state->i2c_cp ||
- !state->i2c_sdp || !state->i2c_hdmi || !state->i2c_edid ||
- !state->i2c_rep) {
- pr_err("%s: Additional I2C Client Fail\n", __func__);
- ret = -EFAULT;
- goto err_exit;
- }
+ state->i2c_csi_txa_addr = IO_REG_CSI_TXA_SADDR >> 1;
+ state->i2c_csi_txb_addr = IO_REG_CSI_TXB_SADDR >> 1;
+ state->i2c_cp_addr = IO_REG_CP_SADDR >> 1;
+ state->i2c_hdmi_addr = IO_REG_HDMI_SADDR >> 1;
+ state->i2c_edid_addr = IO_REG_EDID_SADDR >> 1;
+ state->i2c_sdp_addr = IO_REG_SDP_SADDR >> 1;
+ state->i2c_rep_addr = IO_REG_HDMI_REP_SADDR >> 1;
+ state->i2c_cbus_addr = IO_REG_CBUS_SADDR >> 1;
ret = adv7481_set_edid(state);
ret |= adv7481_set_irq(state);
@@ -465,28 +798,25 @@ err_exit:
}
/* Initialize adv7481 hardware */
-static int adv7481_hw_init(struct adv7481_platform_data *pdata,
- struct adv7481_state *state)
+static int adv7481_hw_init(struct adv7481_state *state)
{
int ret = 0;
- if (!pdata) {
- pr_err("%s: PDATA is NULL\n", __func__);
- return -EFAULT;
- }
-
mutex_lock(&state->mutex);
- if (gpio_is_valid(pdata->rstb_gpio)) {
- ret = gpio_request(pdata->rstb_gpio, "rstb_gpio");
- if (ret) {
- pr_err("%s: Request GPIO Fail %d\n", __func__, ret);
- goto err_exit;
- }
- ret = gpio_direction_output(pdata->rstb_gpio, 0);
- usleep_range(GPIO_HW_DELAY_LOW, GPIO_HW_DELAY_LOW+1000);
- ret = gpio_direction_output(pdata->rstb_gpio, 1);
- usleep_range(GPIO_HW_DELAY_HI, GPIO_HW_DELAY_HI+1000);
+ /* Bring ADV7481 out of reset */
+ ret = gpio_request_array(&state->gpio_array[ADV7481_GPIO_RST], 1);
+ if (ret < 0) {
+ pr_err("%s: Failed to request reset GPIO %d\n", __func__, ret);
+ goto err_exit;
+ }
+ if (gpio_is_valid(state->gpio_array[ADV7481_GPIO_RST].gpio)) {
+ ret |= gpio_direction_output(
+ state->gpio_array[ADV7481_GPIO_RST].gpio, 0);
+ udelay(GPIO_HW_RST_DELAY_LOW);
+ ret |= gpio_direction_output(
+ state->gpio_array[ADV7481_GPIO_RST].gpio, 1);
+ udelay(GPIO_HW_RST_DELAY_HI);
if (ret) {
pr_err("%s: Set GPIO Fail %d\n", __func__, ret);
goto err_exit;
@@ -494,22 +824,21 @@ static int adv7481_hw_init(struct adv7481_platform_data *pdata,
}
/* Only setup IRQ1 for now... */
- if (gpio_is_valid(pdata->irq1_gpio)) {
- ret = gpio_request(pdata->irq1_gpio, "irq_gpio");
- if (ret) {
- pr_err("%s: Failed to request irq_gpio %d\n",
- __func__, ret);
- goto err_exit;
- }
-
- ret = gpio_direction_input(pdata->irq1_gpio);
+ ret = gpio_request_array(&state->gpio_array[ADV7481_GPIO_INT1], 1);
+ if (ret < 0) {
+ pr_err("%s: Failed to request irq_gpio %d\n", __func__, ret);
+ goto err_exit;
+ }
+ if (gpio_is_valid(state->gpio_array[ADV7481_GPIO_INT1].gpio)) {
+ ret |= gpio_direction_input(
+ state->gpio_array[ADV7481_GPIO_INT1].gpio);
if (ret) {
pr_err("%s: Failed gpio_direction irq %d\n",
__func__, ret);
goto err_exit;
}
-
- state->irq = gpio_to_irq(pdata->irq1_gpio);
+ state->irq = gpio_to_irq(
+ state->gpio_array[ADV7481_GPIO_INT1].gpio);
if (state->irq) {
ret = request_irq(state->irq, adv7481_irq,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
@@ -519,18 +848,16 @@ static int adv7481_hw_init(struct adv7481_platform_data *pdata,
__func__, ret);
goto err_exit;
}
+ /* disable irq until chip interrupts are programmed */
+ disable_irq(state->irq);
} else {
pr_err("%s: Failed gpio_to_irq %d\n", __func__, ret);
ret = -EINVAL;
goto err_exit;
}
-
- /* disable irq until chip interrupts are programmed */
- disable_irq(state->irq);
-
- INIT_DELAYED_WORK(&state->irq_delayed_work,
- adv7481_irq_delay_work);
}
+ INIT_DELAYED_WORK(&state->irq_delayed_work,
+ adv7481_irq_delay_work);
err_exit:
mutex_unlock(&state->mutex);
@@ -548,31 +875,40 @@ static int adv7481_s_ctrl(struct v4l2_ctrl *ctrl)
pr_debug("Enter %s: id = 0x%x\n", __func__, ctrl->id);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ);
+ temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_VID_ADJ);
temp |= CP_CTR_VID_ADJ_EN;
- ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp);
- ret |= adv7481_wr_byte(state->client,
- CP_REG_BRIGHTNESS, ctrl->val);
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_VID_ADJ, temp);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_BRIGHTNESS, ctrl->val);
break;
case V4L2_CID_CONTRAST:
- temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ);
+ temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_VID_ADJ);
temp |= CP_CTR_VID_ADJ_EN;
- ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp);
- ret |= adv7481_wr_byte(state->client,
- CP_REG_CONTRAST, ctrl->val);
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_VID_ADJ, temp);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_CONTRAST, ctrl->val);
break;
case V4L2_CID_SATURATION:
- temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ);
+ temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_VID_ADJ);
temp |= CP_CTR_VID_ADJ_EN;
- ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp);
- ret |= adv7481_wr_byte(state->client,
- CP_REG_SATURATION, ctrl->val);
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_VID_ADJ, temp);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_SATURATION, ctrl->val);
break;
case V4L2_CID_HUE:
- temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ);
+ temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_VID_ADJ);
temp |= CP_CTR_VID_ADJ_EN;
- ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp);
- ret |= adv7481_wr_byte(state->client, CP_REG_HUE, ctrl->val);
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_VID_ADJ, temp);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CP_REG_HUE, ctrl->val);
break;
default:
break;
@@ -611,6 +947,108 @@ static int adv7481_s_power(struct v4l2_subdev *sd, int on)
return ret;
}
+static int adv7481_set_cec_logical_addr(struct adv7481_state *state, int *la)
+{
+ int rc = 0;
+ uint8_t val;
+
+ if (!la) {
+ pr_err("%s: NULL pointer provided\n", __func__);
+ return -EINVAL;
+ }
+
+ val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ CEC_REG_LOG_ADDR_MASK_ADDR);
+ if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK0)) {
+ val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ CEC_REG_LOGICAL_ADDRESS0_1_ADDR);
+ val = ADV_REG_RSTFIELD(val, CEC_REG_LOGICAL_ADDRESS0);
+ val |= ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS0);
+ rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CEC_REG_LOGICAL_ADDRESS0_1_ADDR, val);
+ } else if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK1)) {
+ val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ CEC_REG_LOGICAL_ADDRESS0_1_ADDR);
+ val = ADV_REG_RSTFIELD(val, CEC_REG_LOGICAL_ADDRESS1);
+ val |= ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS1);
+ rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CEC_REG_LOGICAL_ADDRESS0_1_ADDR, val);
+ } else if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK2)) {
+ val = ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS2);
+ rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CEC_REG_LOGICAL_ADDRESS2_ADDR, val);
+ } else {
+ pr_err("No cec logical address mask set\n");
+ }
+
+ return rc;
+}
+
+static int adv7481_cec_powerup(struct adv7481_state *state, int *powerup)
+{
+ int rc = 0;
+ uint8_t val = 0;
+
+ if (!powerup) {
+ pr_err("%s: NULL pointer provided\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: set power %d\n", __func__, *powerup);
+
+ val = ADV_REG_SETFIELD(*powerup, CEC_REG_CEC_POWER_UP);
+ rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ CEC_REG_CEC_POWER_UP_ADDR, val);
+
+ return rc;
+}
+
+static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct adv7481_state *state = to_state(sd);
+ int *ret_val = arg;
+ long ret = 0;
+ int param = 0;
+
+ pr_debug("Enter %s with command: 0x%x", __func__, cmd);
+
+ if (!sd)
+ return -EINVAL;
+
+ switch (cmd) {
+ case VIDIOC_HDMI_RX_CEC_S_LOGICAL:
+ ret = adv7481_set_cec_logical_addr(state, arg);
+ break;
+ case VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL:
+ ret = adv7481_set_cec_logical_addr(state, &param);
+ break;
+ case VIDIOC_HDMI_RX_CEC_G_PHYSICAL:
+ if (ret_val) {
+ *ret_val = 0;
+ } else {
+ pr_err("%s: NULL pointer provided\n", __func__);
+ ret = -EINVAL;
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_G_CONNECTED:
+ if (ret_val) {
+ *ret_val = state->cec_detected;
+ } else {
+ pr_err("%s: NULL pointer provided\n", __func__);
+ ret = -EINVAL;
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_S_ENABLE:
+ ret = adv7481_cec_powerup(state, arg);
+ break;
+ default:
+ pr_err("Not a typewriter! Command: 0x%x", cmd);
+ ret = -ENOTTY;
+ break;
+ }
+ return ret;
+}
+
static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard)
{
int ret = 0;
@@ -620,18 +1058,22 @@ static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard)
if (sd_standard == NULL)
return -EINVAL;
+ /* Select SDP read-only main Map */
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x01);
do {
- sdp_stat = adv7481_rd_byte(state->i2c_sdp,
- SDP_RO_MAIN_STATUS1_ADDR);
+ sdp_stat = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR);
usleep_range(SDP_MIN_SLEEP, SDP_MAX_SLEEP);
timeout++;
- sdp_stat2 = adv7481_rd_byte(state->i2c_sdp,
- SDP_RO_MAIN_STATUS1_ADDR);
+ sdp_stat2 = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR);
} while ((sdp_stat != sdp_stat2) && (timeout < SDP_NUM_TRIES));
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x00);
if (sdp_stat != sdp_stat2) {
- pr_err("%s(%d), adv7481 SDP status unstable: 1\n",
- __func__, __LINE__);
+ pr_err("%s, adv7481 SDP status unstable: 1\n", __func__);
return -ETIMEDOUT;
}
@@ -681,22 +1123,50 @@ static int adv7481_set_cvbs_mode(struct adv7481_state *state)
pr_debug("Enter %s\n", __func__);
state->mode = ADV7481_IP_CVBS_1;
/* cvbs video settings ntsc etc */
- ret = adv7481_wr_byte(state->client, 0x00, 0x30);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x0f, 0x00);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x00, 0x00);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x03, 0x42);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x04, 0x07);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x13, 0x00);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x17, 0x41);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x31, 0x12);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x52, 0xcd);
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0xff);
- val = adv7481_rd_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR);
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ 0x00, 0x30);
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ 0x0e, 0xff);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x0f, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x52, 0xcd);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x00, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x80);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x9c, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x9c, 0xff);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x80, 0x51);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x81, 0x51);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x82, 0x68);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x03, 0x42);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x04, 0x07);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x13, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x17, 0x41);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ 0x31, 0x12);
+
+ val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CSI_PIX_EN_SEL_ADDR);
/* Output of SD core routed to MIPI CSI 4-lane Tx */
- val |= ADV_REG_SETFIELD(0x10, IO_CTRL_CSI4_IN_SEL);
- ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, val);
- /* Enable autodetect */
- ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0x81);
+ val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) |
+ ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) |
+ ADV_REG_SETFIELD(0x2, IO_CTRL_CSI4_IN_SEL);
+
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CSI_PIX_EN_SEL_ADDR, val);
return ret;
}
@@ -713,69 +1183,101 @@ static int adv7481_set_hdmi_mode(struct adv7481_state *state)
* YUV 422 out via TxA CSI: 4-Lane
*/
/* Disable chip powerdown & Enable HDMI Rx block */
- temp = adv7481_rd_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR);
+ temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_PWR_DOWN_CTRL_ADDR);
val = ADV_REG_SETFIELD(1, IO_CTRL_RX_EN) |
ADV_REG_SETFIELD(0, IO_CTRL_RX_PWDN) |
ADV_REG_SETFIELD(0, IO_CTRL_XTAL_PWDN) |
ADV_REG_SETFIELD(0, IO_CTRL_CORE_PWDN) |
ADV_REG_SETFIELD(0, IO_CTRL_MASTER_PWDN);
- ret = adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, val);
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_PWR_DOWN_CTRL_ADDR, val);
/* SDR mode */
- ret |= adv7481_wr_byte(state->client, 0x11, 0x48);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ 0x11, 0x48);
/* Set CP core to YUV out */
- ret |= adv7481_wr_byte(state->client, 0x04, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ 0x04, 0x00);
/* Set CP core to SDR 422 */
- ret |= adv7481_wr_byte(state->client, 0x12, 0xF2);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ 0x12, 0xF2);
/* Saturate both Luma and Chroma values to 254 */
- ret |= adv7481_wr_byte(state->client, 0x17, 0x80);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ 0x17, 0x80);
/* Set CP core to enable AV codes */
- ret |= adv7481_wr_byte(state->client, 0x03, 0x86);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ 0x03, 0x86);
/* ADI RS CP Core: */
- ret |= adv7481_wr_byte(state->i2c_cp, 0x7C, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_cp_addr,
+ 0x7C, 0x00);
/* Set CP core Phase Adjustment */
- ret |= adv7481_wr_byte(state->client, 0x0C, 0xE0);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ 0x0C, 0xE0);
/* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */
- ret |= adv7481_wr_byte(state->client, IO_PAD_CTRLS_ADDR, 0xDD);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_PAD_CTRLS_ADDR, 0xDD);
/* Enable Tx A CSI 4-Lane & data from CP core */
val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) |
ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) |
ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL);
- ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR,
- val);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_CSI_PIX_EN_SEL_ADDR, val);
/* start to configure HDMI Rx once io-map is configured */
/* Enable HDCP 1.1 */
- ret |= adv7481_wr_byte(state->i2c_rep, 0x40, 0x83);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr,
+ 0x40, 0x83);
/* Foreground Channel = A */
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x00, 0x08);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x00, 0x08);
/* ADI Required Write */
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x98, 0xFF);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x99, 0xA3);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9A, 0x00);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9B, 0x0A);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9D, 0x40);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0xCB, 0x09);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x98, 0xFF);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x99, 0xA3);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x9A, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x9B, 0x0A);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x9D, 0x40);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0xCB, 0x09);
/* ADI RS */
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3D, 0x10);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3E, 0x7B);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3F, 0x5E);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4E, 0xFE);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4F, 0x18);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x57, 0xA3);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x58, 0x04);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x85, 0x10);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x3D, 0x10);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x3E, 0x7B);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x3F, 0x5E);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x4E, 0xFE);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x4F, 0x18);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x57, 0xA3);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x58, 0x04);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x85, 0x10);
/* Enable All Terminations */
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x83, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x83, 0x00);
/* ADI RS */
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0xA3, 0x01);
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0xBE, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0xA3, 0x01);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0xBE, 0x00);
/* HPA Manual Enable */
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x6C, 0x01);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x6C, 0x01);
/* HPA Asserted */
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0xF8, 0x01);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0xF8, 0x01);
/* Audio Mute Speed Set to Fastest (Smallest Step Size) */
- ret |= adv7481_wr_byte(state->i2c_hdmi, 0x0F, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ 0x0F, 0x00);
return ret;
}
@@ -860,7 +1362,7 @@ static int adv7481_set_ip_mode(struct adv7481_state *state, int input)
}
static int adv7481_set_op_src(struct adv7481_state *state,
- int output, int input)
+ int output, int input)
{
int ret = 0;
int temp = 0;
@@ -897,10 +1399,11 @@ static int adv7481_set_op_src(struct adv7481_state *state,
default:
ret = -EINVAL;
}
- temp = adv7481_rd_byte(state->client,
+ temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
IO_REG_PWR_DOWN_CTRL_ADDR);
temp |= val;
- adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, temp);
+ adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_REG_PWR_DOWN_CTRL_ADDR, temp);
state->csia_src = input;
break;
case ADV7481_OP_CSIB:
@@ -915,11 +1418,11 @@ static int adv7481_set_op_src(struct adv7481_state *state,
return ret;
}
-static u32 ba_inp_to_adv7481(u32 input)
+static u32 ba_inp_to_adv7481(u32 ba_input)
{
u32 adv_input = ADV7481_IP_HDMI;
- switch (input) {
+ switch (ba_input) {
case BA_IP_CVBS_0:
adv_input = ADV7481_IP_CVBS_1;
break;
@@ -954,6 +1457,42 @@ static u32 ba_inp_to_adv7481(u32 input)
return adv_input;
}
+static u32 adv7481_inp_to_ba(u32 adv_input)
+{
+ u32 ba_input = BA_IP_HDMI_1;
+
+ switch (adv_input) {
+ case ADV7481_IP_CVBS_1:
+ ba_input = BA_IP_CVBS_0;
+ break;
+ case ADV7481_IP_CVBS_2:
+ ba_input = BA_IP_CVBS_1;
+ break;
+ case ADV7481_IP_CVBS_3:
+ ba_input = BA_IP_CVBS_2;
+ break;
+ case ADV7481_IP_CVBS_4:
+ ba_input = BA_IP_CVBS_3;
+ break;
+ case ADV7481_IP_CVBS_5:
+ ba_input = BA_IP_CVBS_4;
+ break;
+ case ADV7481_IP_CVBS_6:
+ ba_input = BA_IP_CVBS_5;
+ break;
+ case ADV7481_IP_HDMI:
+ ba_input = BA_IP_HDMI_1;
+ break;
+ case ADV7481_IP_TTL:
+ ba_input = BA_IP_TTL;
+ break;
+ default:
+ ba_input = BA_IP_HDMI_1;
+ break;
+ }
+ return ba_input;
+}
+
static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input,
u32 output, u32 config)
{
@@ -971,14 +1510,11 @@ static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input,
goto unlock_exit;
}
- if (state->mode != adv_input) {
- ret = adv7481_set_ip_mode(state, adv_input);
- if (ret)
- pr_err("%s: Set input mode failed: %d\n",
- __func__, ret);
- else
- state->mode = adv_input;
- }
+ ret = adv7481_set_ip_mode(state, adv_input);
+ if (ret)
+ pr_err("%s: Set input mode failed: %d\n", __func__, ret);
+ else
+ state->mode = adv_input;
unlock_exit:
mutex_unlock(&state->mutex);
@@ -986,6 +1522,27 @@ unlock_exit:
return ret;
}
+static bool adv7481_is_timing_locked(struct adv7481_state *state)
+{
+ bool ret = false;
+ int val1 = 0;
+ int val2 = 0;
+
+ /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */
+ val1 = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr,
+ IO_HDMI_LVL_RAW_STATUS_3_ADDR);
+ val2 = adv7481_rd_byte(&state->i2c_client, state->i2c_cp_addr,
+ CP_REG_STDI_CH_ADDR);
+
+ if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) &&
+ ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) &&
+ ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) &&
+ ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1))
+ ret = true;
+
+ return ret;
+}
+
static int adv7481_get_hdmi_timings(struct adv7481_state *state,
struct adv7481_vid_params *vid_params,
struct adv7481_hdmi_params *hdmi_params)
@@ -998,13 +1555,15 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state,
pr_debug("Enter %s\n", __func__);
/* Check TMDS PLL Lock and Frequency */
- temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM4_ADDR);
+ temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ HDMI_REG_HDMI_PARAM4_ADDR);
hdmi_params->pll_lock = ADV_REG_GETFIELD(temp1,
HDMI_REG_TMDS_PLL_LOCKED);
if (hdmi_params->pll_lock) {
- temp1 = adv7481_rd_byte(state->i2c_hdmi,
- HDMI_REG_TMDS_FREQ_ADDR);
- temp2 = adv7481_rd_byte(state->i2c_hdmi,
+ temp1 = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_hdmi_addr, HDMI_REG_TMDS_FREQ_ADDR);
+ temp2 = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_hdmi_addr,
HDMI_REG_TMDS_FREQ_FRAC_ADDR);
hdmi_params->tmds_freq = ADV_REG_GETFIELD(temp1,
HDMI_REG_TMDS_FREQ);
@@ -1015,34 +1574,29 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state,
hdmi_params->tmds_freq += ADV_REG_GETFIELD(temp2,
HDMI_REG_TMDS_FREQ_FRAC)*ONE_MHZ_TO_HZ/128;
} else {
- pr_err("%s: PLL not locked return EBUSY\n", __func__);
- return -EBUSY;
+ pr_err("%s(%d): PLL not locked return EBUSY\n",
+ __func__, __LINE__);
+ ret = -EBUSY;
+ goto set_default;
}
- /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */
+ /* Check Timing Lock */
do {
- temp1 = adv7481_rd_byte(state->client,
- IO_HDMI_LVL_RAW_STATUS_3_ADDR);
- temp2 = adv7481_rd_byte(state->i2c_cp,
- CP_REG_STDI_CH_ADDR);
-
- if (ADV_REG_GETFIELD(temp1, IO_DE_REGEN_LCK_RAW) &&
- ADV_REG_GETFIELD(temp1, IO_V_LOCKED_RAW) &&
- ADV_REG_GETFIELD(temp1, IO_TMDSPLL_LCK_A_RAW) &&
- ADV_REG_GETFIELD(temp2, CP_STDI_DVALID_CH1))
+ if (adv7481_is_timing_locked(state))
break;
count++;
usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP);
} while (count < LOCK_NUM_TRIES);
if (count >= LOCK_NUM_TRIES) {
- pr_err("%s(%d), adv7481 HDMI DE regeneration block NOT Locked: 0x%x",
- __func__, __LINE__, temp1);
+ pr_err("%s(%d), HDMI DE regeneration block NOT Locked\n",
+ __func__, __LINE__);
}
/* Check Timing Lock HDMI Map V:0x07[7], H:0x7[5] */
do {
- temp1 = adv7481_rd_byte(state->i2c_hdmi,
+ temp1 = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_hdmi_addr,
HDMI_REG_LINE_WIDTH_1_ADDR);
if (ADV_REG_GETFIELD(temp1, HDMI_VERT_FILTER_LOCKED) &&
@@ -1059,7 +1613,8 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state,
}
/* Check HDMI Parameters */
- temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_FIELD1_HEIGHT1_ADDR);
+ temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ HDMI_REG_FIELD1_HEIGHT1_ADDR);
hdmi_params->color_depth = ADV_REG_GETFIELD(temp1,
HDMI_REG_DEEP_COLOR_MODE);
@@ -1068,22 +1623,25 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state,
HDMI_REG_HDMI_INTERLACED);
fieldfactor = (vid_params->intrlcd == 1) ? 2 : 1;
- temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM5_ADDR);
+ temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ HDMI_REG_HDMI_PARAM5_ADDR);
hdmi_params->pix_rep = ADV_REG_GETFIELD(temp1,
HDMI_REG_PIXEL_REPETITION);
/* Get Active Timing Data HDMI Map H:0x07[4:0] + 0x08[7:0] */
- temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_1_ADDR);
- temp2 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_2_ADDR);
+ temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ HDMI_REG_LINE_WIDTH_1_ADDR);
+ temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
+ HDMI_REG_LINE_WIDTH_2_ADDR);
vid_params->act_pix = (((ADV_REG_GETFIELD(temp1,
HDMI_REG_LINE_WIDTH_1) << 8) & 0x1F00) |
ADV_REG_GETFIELD(temp2,
HDMI_REG_LINE_WIDTH_2));
/* Get Total Timing Data HDMI Map H:0x1E[5:0] + 0x1F[7:0] */
- temp1 = adv7481_rd_byte(state->i2c_hdmi,
+ temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR);
- temp2 = adv7481_rd_byte(state->i2c_hdmi,
+ temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR);
vid_params->tot_pix = (((ADV_REG_GETFIELD(temp1,
HDMI_REG_TOTAL_LINE_WIDTH_1) << 8) & 0x3F00) |
@@ -1091,9 +1649,9 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state,
HDMI_REG_TOTAL_LINE_WIDTH_2));
/* Get Active Timing Data HDMI Map V:0x09[4:0] + 0x0A[7:0] */
- temp1 = adv7481_rd_byte(state->i2c_hdmi,
+ temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_FIELD0_HEIGHT_1_ADDR);
- temp2 = adv7481_rd_byte(state->i2c_hdmi,
+ temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_FIELD0_HEIGHT_2_ADDR);
vid_params->act_lines = (((ADV_REG_GETFIELD(temp1,
HDMI_REG_FIELD0_HEIGHT_1) << 8) & 0x1F00) |
@@ -1101,9 +1659,9 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state,
HDMI_REG_FIELD0_HEIGHT_2));
/* Get Total Timing Data HDMI Map V:0x26[5:0] + 0x27[7:0] */
- temp1 = adv7481_rd_byte(state->i2c_hdmi,
+ temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR);
- temp2 = adv7481_rd_byte(state->i2c_hdmi,
+ temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR);
vid_params->tot_lines = (((ADV_REG_GETFIELD(temp1,
HDMI_REG_FIELD0_TOT_HEIGHT_1) << 8) & 0x3F00) |
@@ -1139,6 +1697,18 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state,
(hdmi_params->pix_rep + 1));
}
+set_default:
+ if (ret) {
+ pr_debug("%s(%d), error %d resort to default fmt\n",
+ __func__, __LINE__, ret);
+ vid_params->act_pix = MAX_DEFAULT_WIDTH;
+ vid_params->act_lines = MAX_DEFAULT_HEIGHT;
+ vid_params->fr_rate = MAX_DEFAULT_FRAME_RATE;
+ vid_params->pix_clk = MAX_DEFAULT_PIX_CLK_HZ;
+ vid_params->intrlcd = 0;
+ ret = 0;
+ }
+
pr_debug("%s(%d), adv7481 TMDS Resolution: %d x %d @ %d fps\n",
__func__, __LINE__,
vid_params->act_pix, vid_params->act_lines,
@@ -1170,15 +1740,22 @@ static int adv7481_query_dv_timings(struct v4l2_subdev *sd,
switch (state->mode) {
case ADV7481_IP_HDMI:
case ADV7481_IP_CVBS_1_HDMI_SIM:
- adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params);
- timings->type = V4L2_DV_BT_656_1120;
- bt_timings->width = vid_params.act_pix;
- bt_timings->height = vid_params.act_lines;
- bt_timings->pixelclock = vid_params.pix_clk;
- bt_timings->interlaced = vid_params.intrlcd ?
+ ret = adv7481_get_hdmi_timings(state, &vid_params,
+ &hdmi_params);
+ if (!ret) {
+ timings->type = V4L2_DV_BT_656_1120;
+ bt_timings->width = vid_params.act_pix;
+ bt_timings->height = vid_params.act_lines;
+ bt_timings->pixelclock = vid_params.pix_clk;
+ bt_timings->interlaced = vid_params.intrlcd ?
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
- if (bt_timings->interlaced == V4L2_DV_INTERLACED)
- bt_timings->height /= 2;
+ if (bt_timings->interlaced == V4L2_DV_INTERLACED)
+ bt_timings->height /= 2;
+ } else {
+ pr_err(
+ "%s: Error in adv7481_get_hdmi_timings. ret %d\n",
+ __func__, ret);
+ }
break;
default:
return -EINVAL;
@@ -1193,10 +1770,24 @@ static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std)
int temp = 0;
struct adv7481_state *state = to_state(sd);
uint8_t tStatus = 0x0;
+ uint32_t count = 0;
pr_debug("Enter %s\n", __func__);
- tStatus = adv7481_rd_byte(state->i2c_sdp, SDP_RO_MAIN_STATUS1_ADDR);
- if (!ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK))
+ /* Select SDP read-only main Map */
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x01);
+ do {
+ tStatus = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR);
+ if (ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK))
+ break;
+ count++;
+ usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP);
+ } while (count < LOCK_NUM_TRIES);
+
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x00);
+ if (count >= LOCK_NUM_TRIES)
pr_err("%s(%d), adv7481 SD Input NOT Locked: 0x%x\n",
__func__, __LINE__, tStatus);
@@ -1245,13 +1836,15 @@ static int adv7481_g_frame_interval(struct v4l2_subdev *sd,
return 0;
}
-static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *fmt)
+static int adv7481_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
{
int ret;
struct adv7481_vid_params vid_params;
struct adv7481_hdmi_params hdmi_params;
struct adv7481_state *state = to_state(sd);
+ struct v4l2_mbus_framefmt *fmt = &format->format;
if (!fmt)
return -EINVAL;
@@ -1267,17 +1860,29 @@ static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd,
switch (state->mode) {
case ADV7481_IP_HDMI:
case ADV7481_IP_CVBS_1_HDMI_SIM:
- adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params);
- fmt->width = vid_params.act_pix;
- fmt->height = vid_params.act_lines;
- if (vid_params.intrlcd)
- fmt->height /= 2;
+ ret = adv7481_get_hdmi_timings(state, &vid_params,
+ &hdmi_params);
+ if (!ret) {
+ fmt->width = vid_params.act_pix;
+ fmt->height = vid_params.act_lines;
+ if (vid_params.intrlcd)
+ fmt->height /= 2;
+ } else {
+ pr_err("%s: Error %d in adv7481_get_hdmi_timings\n",
+ __func__, ret);
+ }
+ break;
+ case ADV7481_IP_CVBS_1:
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->width = 720;
+ fmt->height = 576;
break;
default:
return -EINVAL;
}
mutex_unlock(&state->mutex);
- fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
return ret;
}
@@ -1290,17 +1895,17 @@ static int adv7481_set_audio_spdif(struct adv7481_state *state,
if (on) {
/* Configure I2S_SDATA output pin as an SPDIF output 0x6E[3] */
- val = adv7481_rd_byte(state->i2c_hdmi,
+ val = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_MUX_SPDIF_TO_I2S_ADDR);
val |= ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN);
- ret = adv7481_wr_byte(state->i2c_hdmi,
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val);
} else {
/* Configure I2S_SDATA output pin as an I2S output 0x6E[3] */
- val = adv7481_rd_byte(state->i2c_hdmi,
+ val = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_MUX_SPDIF_TO_I2S_ADDR);
val &= ~ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN);
- ret = adv7481_wr_byte(state->i2c_hdmi,
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr,
HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val);
}
return ret;
@@ -1310,40 +1915,42 @@ static int adv7481_csi_powerdown(struct adv7481_state *state,
enum adv7481_output output)
{
int ret;
- struct i2c_client *csi_map;
+ uint8_t csi_map;
uint8_t val = 0;
pr_debug("Enter %s for output: %d\n", __func__, output);
/* Select CSI TX to configure data */
if (output == ADV7481_OP_CSIA) {
- csi_map = state->i2c_csi_txa;
+ csi_map = state->i2c_csi_txa_addr;
} else if (output == ADV7481_OP_CSIB) {
- csi_map = state->i2c_csi_txb;
+ csi_map = state->i2c_csi_txb_addr;
} else if (output == ADV7481_OP_TTL) {
/* For now use TxA */
- csi_map = state->i2c_csi_txa;
+ csi_map = state->i2c_csi_txa_addr;
} else {
/* Default to TxA */
- csi_map = state->i2c_csi_txa;
+ csi_map = state->i2c_csi_txa_addr;
}
/* CSI Tx: power down DPHY */
- ret = adv7481_wr_byte(csi_map, CSI_REG_TX_DPHY_PWDN_ADDR,
+ ret = adv7481_wr_byte(&state->i2c_client, csi_map,
+ CSI_REG_TX_DPHY_PWDN_ADDR,
ADV_REG_SETFIELD(1, CSI_CTRL_DPHY_PWDN));
/* ADI Required Write */
- ret |= adv7481_wr_byte(csi_map, 0x31, 0x82);
- ret |= adv7481_wr_byte(csi_map, 0x1e, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x82);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x1e, 0x00);
/* CSI TxA: # Lane : Power Off */
val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) |
ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES);
- ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map,
+ CSI_REG_TX_CFG1_ADDR, val);
/*
* ADI Recommended power down sequence
* DPHY and CSI Tx A Power down Sequence
* CSI TxA: MIPI PLL DIS
*/
- ret |= adv7481_wr_byte(csi_map, 0xda, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xda, 0x00);
/* ADI Required Write */
- ret |= adv7481_wr_byte(csi_map, 0xc1, 0x3b);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc1, 0x3b);
pr_debug("Exit %s, ret: %d\n", __func__, ret);
@@ -1354,17 +1961,23 @@ static int adv7481_csi_powerup(struct adv7481_state *state,
enum adv7481_output output)
{
int ret;
- struct i2c_client *csi_map;
+ uint8_t csi_map;
uint8_t val = 0;
uint8_t csi_sel = 0;
pr_debug("Enter %s for output: %d\n", __func__, output);
/* Select CSI TX to configure data */
if (output == ADV7481_OP_CSIA) {
- csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) |
- ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) |
- ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL);
- csi_map = state->i2c_csi_txa;
+ if (state->csia_src == ADV7481_IP_HDMI) {
+ csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) |
+ ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) |
+ ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL);
+ } else {
+ csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) |
+ ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) |
+ ADV_REG_SETFIELD(0x2, IO_CTRL_CSI4_IN_SEL);
+ }
+ csi_map = state->i2c_csi_txa_addr;
} else if (output == ADV7481_OP_CSIB) {
/* Enable 1-Lane MIPI Tx, enable pixel output and
* route SD through Pixel port
@@ -1373,54 +1986,57 @@ static int adv7481_csi_powerup(struct adv7481_state *state,
ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) |
ADV_REG_SETFIELD(1, IO_CTRL_SD_THRU_PIX_OUT) |
ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL);
- csi_map = state->i2c_csi_txb;
+ csi_map = state->i2c_csi_txb_addr;
} else if (output == ADV7481_OP_TTL) {
/* For now use TxA */
- csi_map = state->i2c_csi_txa;
+ csi_map = state->i2c_csi_txa_addr;
} else {
/* Default to TxA */
- csi_map = state->i2c_csi_txa;
+ csi_map = state->i2c_csi_txa_addr;
}
/* Enable Tx A/B CSI #-lane */
- ret = adv7481_wr_byte(state->client,
+ ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
IO_REG_CSI_PIX_EN_SEL_ADDR, csi_sel);
/* TXA MIPI lane settings for CSI */
/* CSI TxA: # Lane : Power Off */
val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) |
ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES);
- ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map,
+ CSI_REG_TX_CFG1_ADDR, val);
/* CSI TxA: Auto D-PHY Timing */
val |= ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS);
- ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map,
+ CSI_REG_TX_CFG1_ADDR, val);
/* DPHY and CSI Tx A */
- ret |= adv7481_wr_byte(csi_map, 0xdb, 0x10);
- ret |= adv7481_wr_byte(csi_map, 0xd6, 0x07);
- ret |= adv7481_wr_byte(csi_map, 0xc4, 0x0a);
- ret |= adv7481_wr_byte(csi_map, 0x71, 0x33);
- ret |= adv7481_wr_byte(csi_map, 0x72, 0x11);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xdb, 0x10);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xd6, 0x07);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc4, 0x0a);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x71, 0x33);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x72, 0x11);
/* CSI TxA: power up DPHY */
- ret |= adv7481_wr_byte(csi_map, 0xf0, 0x00);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xf0, 0x00);
/* ADI Required Write */
- ret |= adv7481_wr_byte(csi_map, 0x31, 0x82);
- ret |= adv7481_wr_byte(csi_map, 0x1e, 0x40);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x82);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x1e, 0x40);
/* adi Recommended power up sequence */
/* DPHY and CSI Tx A Power up Sequence */
/* CSI TxA: MIPI PLL EN */
- ret |= adv7481_wr_byte(csi_map, 0xda, 0x01);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xda, 0x01);
msleep(200);
/* CSI TxA: # MIPI Lane : Power ON */
val = ADV_REG_SETFIELD(0, CSI_CTRL_TX_PWRDN) |
ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS) |
ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES);
- ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map,
+ CSI_REG_TX_CFG1_ADDR, val);
msleep(100);
/* ADI Required Write */
- ret |= adv7481_wr_byte(csi_map, 0xc1, 0x2b);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc1, 0x2b);
msleep(100);
/* ADI Required Write */
- ret |= adv7481_wr_byte(csi_map, 0x31, 0x80);
+ ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x80);
pr_debug("Exit %s, ret: %d\n", __func__, ret);
@@ -1476,42 +2092,43 @@ static int adv7481_g_input_status(struct v4l2_subdev *sd, u32 *status)
{
int ret = 0;
struct adv7481_state *state = to_state(sd);
- uint8_t val1 = 0;
- uint8_t val2 = 0;
+ uint8_t val = 0;
uint32_t count = 0;
+ *status = 0;
pr_debug("Enter %s\n", __func__);
if (ADV7481_IP_HDMI == state->mode) {
- /*
- * Check Timing Lock IO Map Status3:0x71[0] &&
- * 0x71[1] && 0x71[7]
- */
+ /* Check Timing Lock */
do {
- val1 = adv7481_rd_byte(state->client,
- IO_HDMI_LVL_RAW_STATUS_3_ADDR);
- val2 = adv7481_rd_byte(state->i2c_cp,
- CP_REG_STDI_CH_ADDR);
-
- if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) &&
- ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) &&
- ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) &&
- ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1))
+ if (adv7481_is_timing_locked(state))
break;
count++;
usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP);
} while (count < LOCK_NUM_TRIES);
if (count >= LOCK_NUM_TRIES) {
- pr_err("%s(%d), HDMI DE regeneration block NOT Locked: 0x%x, 0x%x",
- __func__, __LINE__, val1, val2);
+ pr_err("%s(%d), HDMI DE regeneration block NOT Locked\n",
+ __func__, __LINE__);
*status |= V4L2_IN_ST_NO_SIGNAL;
}
} else {
- val1 = adv7481_rd_byte(state->i2c_sdp,
- SDP_RO_MAIN_STATUS1_ADDR);
- if (!ADV_REG_GETFIELD(val1, SDP_RO_MAIN_IN_LOCK)) {
+ /* Select SDP read-only main Map */
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x01);
+ do {
+ val = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR);
+ if (ADV_REG_GETFIELD(val, SDP_RO_MAIN_IN_LOCK))
+ break;
+ count++;
+ usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP);
+ } while (count < LOCK_NUM_TRIES);
+
+ adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
+ SDP_RW_MAP_REG, 0x00);
+ if (count >= LOCK_NUM_TRIES) {
pr_err("%s(%d), SD Input NOT Locked: 0x%x\n",
- __func__, __LINE__, val1);
+ __func__, __LINE__, val);
*status |= V4L2_IN_ST_NO_SIGNAL;
}
}
@@ -1530,7 +2147,6 @@ static int adv7481_s_stream(struct v4l2_subdev *sd, int on)
static const struct v4l2_subdev_video_ops adv7481_video_ops = {
.s_routing = adv7481_s_routing,
.g_frame_interval = adv7481_g_frame_interval,
- .g_mbus_fmt = adv7481_g_mbus_fmt,
.querystd = adv7481_query_sd_std,
.g_dv_timings = adv7481_query_dv_timings,
.g_input_status = adv7481_g_input_status,
@@ -1539,6 +2155,11 @@ static const struct v4l2_subdev_video_ops adv7481_video_ops = {
static const struct v4l2_subdev_core_ops adv7481_core_ops = {
.s_power = adv7481_s_power,
+ .ioctl = adv7481_ioctl,
+};
+
+static const struct v4l2_subdev_pad_ops adv7481_pad_ops = {
+ .get_fmt = adv7481_get_fmt,
};
static const struct v4l2_ctrl_ops adv7481_ctrl_ops = {
@@ -1548,10 +2169,13 @@ static const struct v4l2_ctrl_ops adv7481_ctrl_ops = {
static const struct v4l2_subdev_ops adv7481_ops = {
.core = &adv7481_core_ops,
.video = &adv7481_video_ops,
+ .pad = &adv7481_pad_ops,
};
static int adv7481_init_v4l2_controls(struct adv7481_state *state)
{
+ int ret = 0;
+
v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7481_ctrl_ops,
@@ -1565,59 +2189,195 @@ static int adv7481_init_v4l2_controls(struct adv7481_state *state)
state->sd.ctrl_handler = &state->ctrl_hdl;
if (state->ctrl_hdl.error) {
- int err = state->ctrl_hdl.error;
+ ret = state->ctrl_hdl.error;
v4l2_ctrl_handler_free(&state->ctrl_hdl);
- return err;
+ } else {
+ v4l2_ctrl_handler_setup(&state->ctrl_hdl);
}
- v4l2_ctrl_handler_setup(&state->ctrl_hdl);
- return 0;
+ pr_err("%s: Exit with ret: %d\n", __func__, ret);
+ return ret;
+}
+
+static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = {
+ .i2c_read = msm_camera_cci_i2c_read,
+ .i2c_read_seq = msm_camera_cci_i2c_read_seq,
+ .i2c_write = msm_camera_cci_i2c_write,
+ .i2c_write_seq = msm_camera_cci_i2c_write_seq,
+ .i2c_write_table = msm_camera_cci_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_cci_i2c_write_table_w_microdelay,
+ .i2c_util = msm_sensor_cci_i2c_util,
+ .i2c_poll = msm_camera_cci_i2c_poll,
+};
+
+static int adv7481_cci_init(struct adv7481_state *state)
+{
+ struct msm_camera_cci_client *cci_client = NULL;
+ int ret = 0;
+
+ pr_err("%s: Enter\n", __func__);
+
+ state->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl;
+ state->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
+ state->i2c_client.cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ cci_client = state->i2c_client.cci_client;
+ if (!cci_client) {
+ ret = -ENOMEM;
+ goto err_cci_init;
+ }
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ pr_debug("%s cci_subdev: %p\n", __func__, cci_client->cci_subdev);
+ if (!cci_client->cci_subdev) {
+ ret = -EPROBE_DEFER;
+ goto err_cci_init;
+ }
+ cci_client->cci_i2c_master = state->cci_master;
+ cci_client->sid = state->i2c_slave_addr;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->i2c_freq_mode = I2C_CUSTOM_MODE;
+ ret = state->i2c_client.i2c_func_tbl->i2c_util(
+ &state->i2c_client, MSM_CCI_INIT);
+ if (ret < 0)
+ pr_err("%s - cci_init failed\n", __func__);
+ else
+ state->clocks_requested = TRUE;
+
+ pr_debug("%s i2c_client.client: %p\n", __func__,
+ state->i2c_client.client);
+
+err_cci_init:
+ return ret;
}
-static int adv7481_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adv7481_parse_dt(struct platform_device *pdev,
+ struct adv7481_state *state)
+{
+ struct device_node *np = state->dev->of_node;
+ uint32_t i = 0;
+ int gpio_count = 0;
+ struct resource *adv_addr_res = NULL;
+ int ret = 0;
+
+ /* config CCI */
+ ret = of_property_read_u32(np, "qcom,cci-master",
+ &state->cci_master);
+ if (ret < 0 || state->cci_master >= MASTER_MAX) {
+ pr_err("%s: failed to read cci master . ret %d\n",
+ __func__, ret);
+ goto exit;
+ }
+ pr_debug("%s: cci_master: 0x%x\n", __func__, state->cci_master);
+ adv_addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!adv_addr_res) {
+ pr_err("%s: failed to read adv7481 resource.\n", __func__);
+ goto exit;
+ }
+ state->i2c_slave_addr = adv_addr_res->start;
+ pr_debug("%s: i2c_slave_addr: 0x%x\n", __func__, state->i2c_slave_addr);
+ state->i2c_io_addr = (uint8_t)state->i2c_slave_addr;
+
+ gpio_count = of_gpio_count(np);
+ if (gpio_count != ADV7481_GPIO_MAX) {
+ ret = -EFAULT;
+ pr_err("%s: dt gpio count %d doesn't match required. ret %d\n",
+ __func__, gpio_count, ret);
+ goto exit;
+ }
+ for (i = 0; i < ADV7481_GPIO_MAX; i++) {
+ state->gpio_array[i].gpio = of_get_gpio_flags(np, i,
+ (enum of_gpio_flags *)&state->gpio_array[i].flags);
+ if (!gpio_is_valid(state->gpio_array[i].gpio)) {
+ pr_err("invalid gpio setting for index %d\n", i);
+ ret = -EFAULT;
+ goto exit;
+ }
+ pr_debug("%s: gpio_array[%d] = %d flag = %ld\n", __func__, i,
+ state->gpio_array[i].gpio, state->gpio_array[i].flags);
+ }
+
+exit:
+ return ret;
+}
+
+static const struct of_device_id adv7481_id[] = {
+ { .compatible = "qcom,adv7481", },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, adv7481_id);
+
+static int adv7481_probe(struct platform_device *pdev)
{
struct adv7481_state *state;
- struct adv7481_platform_data *pdata = NULL;
+ const struct of_device_id *device_id;
struct v4l2_subdev *sd;
- struct v4l2_ctrl_handler *hdl;
int ret;
- pr_debug("Attempting to probe...\n");
- /* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE_DATA)) {
- pr_err("%s %s Check i2c Functionality Fail\n",
- __func__, client->name);
- ret = -EIO;
+ device_id = of_match_device(adv7481_id, &pdev->dev);
+ if (!device_id) {
+ pr_err("%s: device_id is NULL\n", __func__);
+ ret = -ENODEV;
goto err;
}
- v4l_info(client, "chip found @ 0x%02x (%s)\n",
- client->addr, client->adapter->name);
/* Create 7481 State */
- state = devm_kzalloc(&client->dev,
- sizeof(struct adv7481_state), GFP_KERNEL);
+ state = devm_kzalloc(&pdev->dev,
+ sizeof(struct adv7481_state), GFP_KERNEL);
if (state == NULL) {
ret = -ENOMEM;
- pr_err("Check Kzalloc Fail\n");
- goto err_mem;
+ goto err;
}
- state->client = client;
+ platform_set_drvdata(pdev, state);
+ state->dev = &pdev->dev;
+
mutex_init(&state->mutex);
+ ret = adv7481_parse_dt(pdev, state);
+ if (ret < 0) {
+ pr_err("Error parsing dt tree\n");
+ goto err_mem_free;
+ }
- /* Get and Check Platform Data */
- pdata = (struct adv7481_platform_data *) client->dev.platform_data;
- if (!pdata) {
- ret = -ENOMEM;
- pr_err("Getting Platform data failed\n");
- goto err_mem;
+ ret = adv7481_cci_init(state);
+ if (ret < 0) {
+ pr_err("%s: failed adv7481_cci_init ret %d\n", __func__, ret);
+ goto err_mem_free;
+ }
+
+ /* config VREG */
+ ret = msm_camera_get_dt_vreg_data(pdev->dev.of_node,
+ &(state->cci_vreg), &(state->regulator_count));
+ if (ret < 0) {
+ pr_err("%s:cci get_dt_vreg failed\n", __func__);
+ goto err_mem_free;
+ }
+
+ ret = msm_camera_config_vreg(&pdev->dev, state->cci_vreg,
+ state->regulator_count, NULL, 0,
+ &state->cci_reg_ptr[0], 1);
+ if (ret < 0) {
+ pr_err("%s:cci config_vreg failed\n", __func__);
+ goto err_mem_free;
+ }
+
+ ret = msm_camera_enable_vreg(&pdev->dev, state->cci_vreg,
+ state->regulator_count, NULL, 0,
+ &state->cci_reg_ptr[0], 1);
+ if (ret < 0) {
+ pr_err("%s:cci enable_vreg failed\n", __func__);
+ goto err_mem_free;
}
+ pr_debug("%s - VREG Initialized...\n", __func__);
- /* Configure and Register V4L2 I2C Sub-device */
+ /* Configure and Register V4L2 Sub-device */
sd = &state->sd;
- v4l2_i2c_subdev_init(sd, client, &adv7481_ops);
+ v4l2_subdev_init(sd, &adv7481_ops);
+ sd->owner = pdev->dev.driver->owner;
+ v4l2_set_subdevdata(sd, state);
+ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
state->sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
@@ -1627,103 +2387,136 @@ static int adv7481_probe(struct i2c_client *client,
ret = media_entity_init(&state->sd.entity, 1, &state->pad, 0);
if (ret) {
ret = -EIO;
- pr_err("Media entity init failed\n");
+ pr_err("%s(%d): Media entity init failed\n",
+ __func__, __LINE__);
goto err_media_entity;
}
/* Initialize HW Config */
- ret = adv7481_hw_init(pdata, state);
+ ret = adv7481_hw_init(state);
if (ret) {
ret = -EIO;
- pr_err("HW Initialisation Failed\n");
+ pr_err("%s: HW Initialisation Failed\n", __func__);
goto err_media_entity;
}
/* Register V4l2 Control Functions */
- hdl = &state->ctrl_hdl;
- v4l2_ctrl_handler_init(hdl, 4);
- adv7481_init_v4l2_controls(state);
+ ret = adv7481_init_v4l2_controls(state);
+ if (ret) {
+ pr_err("%s: V4L2 Controls Initialisation Failed %d\n",
+ __func__, ret);
+ }
- /* Initials ADV7481 State Settings */
+ /* Initial ADV7481 State Settings */
state->tx_auto_params = ADV7481_AUTO_PARAMS;
- state->tx_lanes = ADV7481_MIPI_2LANE;
/* Initialize SW Init Settings and I2C sub maps 7481 */
- ret = adv7481_dev_init(state, client);
+ ret = adv7481_dev_init(state);
if (ret) {
ret = -EIO;
- pr_err("SW Initialisation Failed\n");
+ pr_err("%s(%d): SW Initialisation Failed\n",
+ __func__, __LINE__);
goto err_media_entity;
}
- /* Set hdmi settings */
- ret = adv7481_set_hdmi_mode(state);
-
/* BA registration */
- ret |= msm_ba_register_subdev_node(sd);
+ ret = msm_ba_register_subdev_node(sd);
if (ret) {
ret = -EIO;
- pr_err("BA INIT FAILED\n");
+ pr_err("%s: BA init failed\n", __func__);
goto err_media_entity;
}
+ enable_irq(state->irq);
pr_debug("Probe successful!\n");
return ret;
err_media_entity:
media_entity_cleanup(&sd->entity);
-err_mem:
- kfree(state);
+
+err_mem_free:
+ adv7481_release_cci_clks(state);
+ devm_kfree(&pdev->dev, state);
+
err:
- if (!ret)
- ret = 1;
return ret;
}
-static int adv7481_remove(struct i2c_client *client)
+static int adv7481_remove(struct platform_device *pdev)
{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct adv7481_state *state = to_state(sd);
+ struct adv7481_state *state = platform_get_drvdata(pdev);
- msm_ba_unregister_subdev_node(sd);
- v4l2_device_unregister_subdev(sd);
- media_entity_cleanup(&sd->entity);
+ msm_ba_unregister_subdev_node(&state->sd);
+ v4l2_device_unregister_subdev(&state->sd);
+ media_entity_cleanup(&state->sd.entity);
v4l2_ctrl_handler_free(&state->ctrl_hdl);
+ adv7481_reset_irq(state);
if (state->irq > 0)
free_irq(state->irq, state);
- i2c_unregister_device(state->i2c_csi_txa);
- i2c_unregister_device(state->i2c_csi_txb);
- i2c_unregister_device(state->i2c_hdmi);
- i2c_unregister_device(state->i2c_edid);
- i2c_unregister_device(state->i2c_cp);
- i2c_unregister_device(state->i2c_sdp);
- i2c_unregister_device(state->i2c_rep);
+ cancel_delayed_work(&state->irq_delayed_work);
mutex_destroy(&state->mutex);
- kfree(state);
+ devm_kfree(&pdev->dev, state);
return 0;
}
-static const struct i2c_device_id adv7481_id[] = {
- { DRIVER_NAME, 0 },
- {},
-};
-MODULE_DEVICE_TABLE(i2c, adv7481_id);
+#ifdef CONFIG_PM_SLEEP
+static int adv7481_suspend(struct device *dev)
+{
+ struct adv7481_state *state;
+ int ret;
+
+ state = (struct adv7481_state *)dev_get_drvdata(dev);
+
+ /* release CCI clocks */
+ ret = adv7481_release_cci_clks(state);
+ if (ret)
+ pr_err("%s: adv7481 release cci clocks failed\n", __func__);
+ else
+ pr_debug("released cci clocks in suspend");
+
+ return 0;
+}
+
+static int adv7481_resume(struct device *dev)
+{
+ struct adv7481_state *state;
+ int ret;
+
+ state = (struct adv7481_state *)dev_get_drvdata(dev);
+
+ /* Request CCI clocks */
+ ret = adv7481_request_cci_clks(state);
+ if (ret)
+ pr_err("%s: adv7481 request cci clocks failed\n", __func__);
+ else
+ pr_debug("requested cci clocks in resume");
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(adv7481_pm_ops, adv7481_suspend, adv7481_resume);
+#define ADV7481_PM_OPS (&adv7481_pm_ops)
+#else
+#define ADV7481_PM_OPS NULL
+#endif
-static struct i2c_driver adv7481_driver = {
+static struct platform_driver adv7481_driver = {
.driver = {
.owner = THIS_MODULE,
.name = KBUILD_MODNAME,
+ .of_match_table = adv7481_id,
+ .pm = ADV7481_PM_OPS,
},
.probe = adv7481_probe,
.remove = adv7481_remove,
- .id_table = adv7481_id,
};
-module_i2c_driver(adv7481_driver);
+module_driver(adv7481_driver, platform_driver_register,
+ platform_driver_unregister);
MODULE_DESCRIPTION("ADI ADV7481 HDMI/MHL/SD video receiver");
diff --git a/drivers/media/i2c/adv7481_reg.h b/drivers/media/i2c/adv7481_reg.h
index a4e14fa18e0a..60b1301abbe6 100644
--- a/drivers/media/i2c/adv7481_reg.h
+++ b/drivers/media/i2c/adv7481_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,329 +19,451 @@
#define ADV_REG_GETFIELD(val, field) \
(((val) & (field##_BMSK)) >> (field##_SHFT))
+#define ADV_REG_RSTFIELD(val, field) \
+ ((val) & ~((field##_BMSK) << (field##_SHFT)))
+
/* IO Map Registers */
-#define IO_REG_MAIN_RST_ADDR 0xFF
-#define IO_REG_MAIN_RST_VALUE 0xFF
-
-#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00
-#define IO_CTRL_RX_EN_BMSK 0x0040
-#define IO_CTRL_RX_EN_SHFT 6
-#define IO_CTRL_RX_PWDN_BMSK 0x0020
-#define IO_CTRL_RX_PWDN_SHFT 5
-#define IO_CTRL_XTAL_PWDN_BMSK 0x0004
-#define IO_CTRL_XTAL_PWDN_SHFT 2
-#define IO_CTRL_CORE_PWDN_BMSK 0x0002
-#define IO_CTRL_CORE_PWDN_SHFT 1
-#define IO_CTRL_MASTER_PWDN_BMSK 0x0001
-#define IO_CTRL_MASTER_PWDN_SHFT 0
-
-#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01
-#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080
-#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7
-#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040
-#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6
-#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F
-#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0
-
-#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02
-#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF
-#define IO_PROG_XTAL_FREQ_LOW_SHFT 0
-
-#define IO_REG_CP_VID_STD_ADDR 0x05
-
-#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10
-#define IO_CTRL_CSI4_EN_BMSK 0x0080
-#define IO_CTRL_CSI4_EN_SHFT 7
-#define IO_CTRL_CSI1_EN_BMSK 0x0040
-#define IO_CTRL_CSI1_EN_SHFT 6
-#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020
-#define IO_CTRL_PIX_OUT_EN_SHFT 5
-#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010
-#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4
-#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C
-#define IO_CTRL_CSI4_IN_SEL_SHFT 2
-
-#define IO_PAD_CTRLS_ADDR 0x0E
-
-#define IO_REG_I2C_CFG_ADDR 0xF2
-#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01
-
-#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01
-
-#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71
-#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080
-#define IO_TMDSPLL_LCK_A_RAW_SHFT 7
-#define IO_CABLE_DET_A_RAW_BMSK 0x0040
-#define IO_CABLE_DET_A_RAW_SHFT 6
-#define IO_V_LOCKED_RAW_BMSK 0x0002
-#define IO_V_LOCKED_RAW_SHFT 1
-#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001
-#define IO_DE_REGEN_LCK_RAW_SHFT 0
+#define IO_REG_MAIN_RST_ADDR 0xFF
+#define IO_REG_MAIN_RST_VALUE 0xFF
+
+#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00
+#define IO_CTRL_RX_EN_BMSK 0x0040
+#define IO_CTRL_RX_EN_SHFT 6
+#define IO_CTRL_RX_PWDN_BMSK 0x0020
+#define IO_CTRL_RX_PWDN_SHFT 5
+#define IO_CTRL_XTAL_PWDN_BMSK 0x0004
+#define IO_CTRL_XTAL_PWDN_SHFT 2
+#define IO_CTRL_CORE_PWDN_BMSK 0x0002
+#define IO_CTRL_CORE_PWDN_SHFT 1
+#define IO_CTRL_MASTER_PWDN_BMSK 0x0001
+#define IO_CTRL_MASTER_PWDN_SHFT 0
+
+#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01
+#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080
+#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7
+#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040
+#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6
+#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F
+#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0
+
+#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02
+#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF
+#define IO_PROG_XTAL_FREQ_LOW_SHFT 0
+
+#define IO_REG_CP_VID_STD_ADDR 0x05
+
+#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10
+#define IO_CTRL_CSI4_EN_BMSK 0x0080
+#define IO_CTRL_CSI4_EN_SHFT 7
+#define IO_CTRL_CSI1_EN_BMSK 0x0040
+#define IO_CTRL_CSI1_EN_SHFT 6
+#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020
+#define IO_CTRL_PIX_OUT_EN_SHFT 5
+#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010
+#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4
+#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C
+#define IO_CTRL_CSI4_IN_SEL_SHFT 2
+
+#define IO_PAD_CTRLS_ADDR 0x0E
+#define IO_PAD_FILTER_CTRLS_ADDR 0x0F
+
+#define IO_REG_I2C_CFG_ADDR 0xF2
+#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01
+
+#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01
/* Interrupts */
-#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72
-#define IO_CABLE_DET_A_ST_BMSK 0x0040
-#define IO_CABLE_DET_A_ST_SHFT 6
-
-#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73
-#define IO_CABLE_DET_A_CLR_BMSK 0x0040
-#define IO_CABLE_DET_A_CLR_SHFT 6
-
-#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74
-#define IO_CABLE_DET_A_MB2_BMSK 0x0040
-#define IO_CABLE_DET_A_MB2_SHFT 6
-
-#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75
-#define IO_CABLE_DET_A_MB1_BMSK 0x0040
-#define IO_CABLE_DET_A_MB1_SHFT 6
-
-#define IO_REG_PAD_CTRL_1_ADDR 0x1D
-#define IO_PDN_INT1_BMSK 0x0080
-#define IO_PDN_INT1_SHFT 7
-#define IO_PDN_INT2_BMSK 0x0040
-#define IO_PDN_INT2_SHFT 6
-#define IO_PDN_INT3_BMSK 0x0020
-#define IO_PDN_INT3_SHFT 5
-#define IO_INV_LLC_BMSK 0x0010
-#define IO_INV_LLC_SHFT 4
-#define IO_DRV_LLC_PAD_BMSK 0x000C
-#define IO_DRV_LLC_PAD_SHFT 2
-
-#define IO_REG_INT_RAW_STATUS_ADDR 0x3F
-
-#define IO_REG_INT1_CONF_ADDR 0x40
-#define IO_INTRQ_DUR_SEL_BMSK 0x00C0
-#define IO_INTRQ_DUR_SEL_SHFT 6
-#define IO_INTRQ_OP_SEL_BMSK 0x0003
-#define IO_INTRQ_OP_SEL_SHFT 0
-
-#define IO_REG_INT2_CONF_ADDR 0x41
-#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0
-#define IO_INTRQ2_DUR_SEL_SHFT 6
-#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020
-#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5
-#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008
-#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3
-#define IO_INT2_EN_BMSK 0x0004
-#define IO_INT2_EN_SHFT 2
-#define IO_INTRQ2_OP_SEL_BMSK 0x0003
-#define IO_INTRQ2_OP_SEL_SHFT 0
-
-#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43
-#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44
-#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45
-
-#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47
-#define IO_CP_LOCK_CP_MB1_BMSK 0x0080
-#define IO_CP_LOCK_CP_MB1_SHFT 7
-#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040
-#define IO_CP_UNLOCK_CP_MB1_SHFT 6
-#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020
-#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5
-#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002
-#define IO_MPU_STIM_INTRQ_MB1_SHFT 1
-#define IO_INT_SD_MB1_BMSK 0x0001
-#define IO_INT_SD_MB1_SHFT 0
+#define IO_HDMI_LVL_INT_CLEAR_1_ADDR 0x69
+
+#define IO_HDMI_LVL_INT_MASKB_1_ADDR 0x6B
+#define IO_AVI_INFO_MB1_BMSK 0x0001
+#define IO_AVI_INFO_MB1_SHFT 0
+
+#define IO_HDMI_LVL_INT_CLEAR_2_ADDR 0x6E
+
+#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71
+#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080
+#define IO_TMDSPLL_LCK_A_RAW_SHFT 7
+#define IO_CABLE_DET_A_RAW_BMSK 0x0040
+#define IO_CABLE_DET_A_RAW_SHFT 6
+#define IO_V_LOCKED_RAW_BMSK 0x0002
+#define IO_V_LOCKED_RAW_SHFT 1
+#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001
+#define IO_DE_REGEN_LCK_RAW_SHFT 0
+
+#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72
+#define IO_CABLE_DET_A_ST_BMSK 0x0040
+#define IO_CABLE_DET_A_ST_SHFT 6
+#define IO_V_LOCKED_ST_BMSK 0x0002
+#define IO_V_LOCKED_ST_SHFT 1
+#define IO_DE_REGEN_LCK_ST_BMSK 0x0001
+#define IO_DE_REGEN_LCK_ST_SHFT 0
+
+#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73
+#define IO_CABLE_DET_A_CLR_BMSK 0x0040
+#define IO_CABLE_DET_A_CLR_SHFT 6
+
+#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74
+#define IO_CABLE_DET_A_MB2_BMSK 0x0040
+#define IO_CABLE_DET_A_MB2_SHFT 6
+
+#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75
+#define IO_CABLE_DET_A_MB1_BMSK 0x0040
+#define IO_CABLE_DET_A_MB1_SHFT 6
+#define IO_V_LOCKED_MB1_BMSK 0x0002
+#define IO_V_LOCKED_MB1_SHFT 1
+#define IO_DE_REGEN_LCK_MB1_BMSK 0x0001
+#define IO_DE_REGEN_LCK_MB1_SHFT 0
+
+#define IO_HDMI_EDG_RAW_STATUS_1_ADDR 0x80
+#define IO_NEW_AVI_INFO_RAW_BMSK 0x0001
+#define IO_NEW_AVI_INFO_RAW_SHFT 0
+
+#define IO_HDMI_EDG_INT_STATUS_1_ADDR 0x81
+#define IO_NEW_AVI_INFO_ST_BMSK 0x0001
+#define IO_NEW_AVI_INFO_ST_SHFT 0
+
+#define IO_HDMI_EDG_INT_CLEAR_1_ADDR 0x82
+#define IO_NEW_AVI_INFO_CLR_BMSK 0x0001
+#define IO_NEW_AVI_INFO_CLR_SHFT 0
+
+#define IO_HDMI_EDG_INT2_MASKB_1_ADDR 0x83
+#define IO_NEW_AVI_INFO_MB2_BMSK 0x0001
+#define IO_NEW_AVI_INFO_MB2_SHFT 0
+
+#define IO_HDMI_EDG_INT_MASKB_1_ADDR 0x84
+#define IO_NEW_AVI_INFO_MB1_BMSK 0x0001
+#define IO_NEW_AVI_INFO_MB1_SHFT 0
+
+#define IO_HDMI_EDG_INT_CLEAR_2_ADDR 0x87
+#define IO_HDMI_EDG_INT_CLEAR_3_ADDR 0x8C
+
+#define IO_REG_PAD_CTRL_1_ADDR 0x1D
+#define IO_PDN_INT1_BMSK 0x0080
+#define IO_PDN_INT1_SHFT 7
+#define IO_PDN_INT2_BMSK 0x0040
+#define IO_PDN_INT2_SHFT 6
+#define IO_PDN_INT3_BMSK 0x0020
+#define IO_PDN_INT3_SHFT 5
+#define IO_INV_LLC_BMSK 0x0010
+#define IO_INV_LLC_SHFT 4
+#define IO_DRV_LLC_PAD_BMSK 0x000C
+#define IO_DRV_LLC_PAD_SHFT 2
+
+#define IO_REG_INT_RAW_STATUS_ADDR 0x3F
+#define IO_INT_CEC_ST_BMSK 0x0010
+#define IO_INT_CEC_ST_SHFT 4
+#define IO_INT_HDMI_ST_BMSK 0x0008
+#define IO_INT_HDMI_ST_SHFT 3
+#define IO_INTRQ3_RAW_BMSK 0x0004
+#define IO_INTRQ3_RAW_SHFT 2
+#define IO_INTRQ2_RAW_BMSK 0x0002
+#define IO_INTRQ2_RAW_SHFT 1
+#define IO_INTRQ1_RAW_BMSK 0x0001
+#define IO_INTRQ1_RAW_SHFT 0
+
+#define IO_REG_INT1_CONF_ADDR 0x40
+#define IO_INTRQ_DUR_SEL_BMSK 0x00C0
+#define IO_INTRQ_DUR_SEL_SHFT 6
+#define IO_INTRQ_OP_SEL_BMSK 0x0003
+#define IO_INTRQ_OP_SEL_SHFT 0
+
+#define IO_REG_INT2_CONF_ADDR 0x41
+#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0
+#define IO_INTRQ2_DUR_SEL_SHFT 6
+#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020
+#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5
+#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008
+#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3
+#define IO_INT2_EN_BMSK 0x0004
+#define IO_INT2_EN_SHFT 2
+#define IO_INTRQ2_OP_SEL_BMSK 0x0003
+#define IO_INTRQ2_OP_SEL_SHFT 0
+
+#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43
+#define IO_CP_LOCK_CP_RAW_BMSK 0x0080
+#define IO_CP_LOCK_CP_RAW_SHFT 7
+#define IO_CP_UNLOCK_CP_RAW_BMSK 0x0040
+#define IO_CP_UNLOCK_CP_RAW_SHFT 6
+#define IO_VMUTE_REQUEST_HDMI_RAW_BMSK 0x0020
+#define IO_VMUTE_REQUEST_HDMI_RAW_SHFT 5
+#define IO_MPU_STIM_INTRQ_RAW_BMSK 0x0002
+#define IO_MPU_STIM_INTRQ_RAW_SHFT 1
+#define IO_INT_SD_RAW_BMSK 0x0001
+#define IO_INT_SD_RAW_SHFT 0
+
+#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44
+#define IO_CP_LOCK_CP_ST_BMSK 0x0080
+#define IO_CP_LOCK_CP_ST_SHFT 7
+#define IO_CP_UNLOCK_CP_ST_BMSK 0x0040
+#define IO_CP_UNLOCK_CP_ST_SHFT 6
+#define IO_VMUTE_REQUEST_HDMI_ST_BMSK 0x0020
+#define IO_VMUTE_REQUEST_HDMI_ST_SHFT 5
+#define IO_MPU_STIM_INTRQ_ST_BMSK 0x0002
+#define IO_MPU_STIM_INTRQ_ST_SHFT 1
+#define IO_INT_SD_ST_BMSK 0x0001
+#define IO_INT_SD_ST_SHFT 0
+
+#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45
+
+#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47
+#define IO_CP_LOCK_CP_MB1_BMSK 0x0080
+#define IO_CP_LOCK_CP_MB1_SHFT 7
+#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040
+#define IO_CP_UNLOCK_CP_MB1_SHFT 6
+#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020
+#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5
+#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002
+#define IO_MPU_STIM_INTRQ_MB1_SHFT 1
+#define IO_INT_SD_MB1_BMSK 0x0001
+#define IO_INT_SD_MB1_SHFT 0
+
+#define IO_REG_CHIP_REV_ID_1_ADDR 0xDF
+#define IO_REG_CHIP_REV_ID_2_ADDR 0xE0
/* Offsets */
-#define IO_REG_DPLL_ADDR 0xF3
-#define IO_REG_CP_ADDR 0xF4
-#define IO_REG_HDMI_ADDR 0xF5
-#define IO_REG_EDID_ADDR 0xF6
-#define IO_REG_HDMI_REP_ADDR 0xF7
-#define IO_REG_HDMI_INF_ADDR 0xF8
-#define IO_REG_CBUS_ADDR 0xF9
-#define IO_REG_CEC_ADDR 0xFA
-#define IO_REG_SDP_ADDR 0xFB
-#define IO_REG_CSI_TXB_ADDR 0xFC
-#define IO_REG_CSI_TXA_ADDR 0xFD
+#define IO_REG_DPLL_ADDR 0xF3
+#define IO_REG_CP_ADDR 0xF4
+#define IO_REG_HDMI_ADDR 0xF5
+#define IO_REG_EDID_ADDR 0xF6
+#define IO_REG_HDMI_REP_ADDR 0xF7
+#define IO_REG_HDMI_INF_ADDR 0xF8
+#define IO_REG_CBUS_ADDR 0xF9
+#define IO_REG_CEC_ADDR 0xFA
+#define IO_REG_SDP_ADDR 0xFB
+#define IO_REG_CSI_TXB_ADDR 0xFC
+#define IO_REG_CSI_TXA_ADDR 0xFD
/* Sub Address Map Locations */
-#define IO_REG_DPLL_SADDR 0x4C
-#define IO_REG_CP_SADDR 0x44
-#define IO_REG_HDMI_SADDR 0x74
-#define IO_REG_EDID_SADDR 0x78
-#define IO_REG_HDMI_REP_SADDR 0x64
-#define IO_REG_HDMI_INF_SADDR 0x62
-#define IO_REG_CBUS_SADDR 0xF0
-#define IO_REG_CEC_SADDR 0x82
-#define IO_REG_SDP_SADDR 0xF2
-#define IO_REG_CSI_TXB_SADDR 0x90
-#define IO_REG_CSI_TXA_SADDR 0x94
+#define IO_REG_DPLL_SADDR 0x4C
+#define IO_REG_CP_SADDR 0x44
+#define IO_REG_HDMI_SADDR 0x74
+#define IO_REG_EDID_SADDR 0x78
+#define IO_REG_HDMI_REP_SADDR 0x64
+#define IO_REG_HDMI_INF_SADDR 0x62
+#define IO_REG_CBUS_SADDR 0xF0
+#define IO_REG_CEC_SADDR 0x82
+#define IO_REG_SDP_SADDR 0xF2
+#define IO_REG_CSI_TXB_SADDR 0x90
+#define IO_REG_CSI_TXA_SADDR 0x94
/* HDMI Map Registers */
-#define HDMI_REG_HDMI_PARAM4_ADDR 0x04
-#define HDMI_REG_AV_MUTE_BMSK 0x0040
-#define HDMI_REG_AV_MUTE_SHFT 6
-#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002
-#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1
-#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001
-#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0
-
-#define HDMI_REG_HDMI_PARAM5_ADDR 0x05
-#define HDMI_REG_HDMI_MODE_BMSK 0x0080
-#define HDMI_REG_TMDS_FREQ_0_SHFT 7
-#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040
-#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6
-#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020
-#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5
-#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010
-#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4
-#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F
-#define HDMI_REG_PIXEL_REPETITION_SHFT 0
-
-#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07
-#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080
-#define HDMI_VERT_FILTER_LOCKED_SHFT 7
-#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040
-#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6
-#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020
-#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5
-#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F
-#define HDMI_REG_LINE_WIDTH_1_SHFT 0
-
-#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08
-#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF
-#define HDMI_REG_LINE_WIDTH_2_SHFT 0
-
-#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09
-#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F
-#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0
-#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A
-#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF
-#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0
-
-#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B
-#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0
-#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6
-#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020
-#define HDMI_REG_HDMI_INTERLACED_SHFT 5
-
-#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E
-#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F
-#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0
-
-#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F
-#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF
-#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0
-
-#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26
-#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F
-#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0
-
-#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27
-#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF
-#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0
-
-#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48
-#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040
-#define HDMI_DIS_CABLE_DET_RST_SHFT 6
-
-#define HDMI_REG_TMDS_FREQ_ADDR 0x51
-#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF
-#define HDMI_REG_TMDS_FREQ_SHFT 0
-
-#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52
-#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080
-#define HDMI_REG_TMDS_FREQ_0_SHFT 7
-#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F
-#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0
-
-#define HDMI_REG_RST_CTRLS_ADDR 0x5A
-#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008
-#define HDMI_HDCP_REPT_EDID_RST_SHFT 3
-
-#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E
-#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008
-#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3
+#define HDMI_REG_HDMI_PARAM4_ADDR 0x04
+#define HDMI_REG_AV_MUTE_BMSK 0x0040
+#define HDMI_REG_AV_MUTE_SHFT 6
+#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002
+#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1
+#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001
+#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0
+
+#define HDMI_REG_HDMI_PARAM5_ADDR 0x05
+#define HDMI_REG_HDMI_MODE_BMSK 0x0080
+#define HDMI_REG_TMDS_FREQ_0_SHFT 7
+#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040
+#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6
+#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020
+#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5
+#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010
+#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4
+#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F
+#define HDMI_REG_PIXEL_REPETITION_SHFT 0
+
+#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07
+#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080
+#define HDMI_VERT_FILTER_LOCKED_SHFT 7
+#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040
+#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6
+#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020
+#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5
+#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F
+#define HDMI_REG_LINE_WIDTH_1_SHFT 0
+
+#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08
+#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF
+#define HDMI_REG_LINE_WIDTH_2_SHFT 0
+
+#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09
+#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F
+#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0
+#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A
+#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF
+#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0
+
+#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B
+#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0
+#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6
+#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020
+#define HDMI_REG_HDMI_INTERLACED_SHFT 5
+
+#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E
+#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F
+#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0
+
+#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F
+#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF
+#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0
+
+#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26
+#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F
+#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0
+
+#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27
+#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF
+#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0
+
+#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48
+#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040
+#define HDMI_DIS_CABLE_DET_RST_SHFT 6
+
+#define HDMI_REG_TMDS_FREQ_ADDR 0x51
+#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF
+#define HDMI_REG_TMDS_FREQ_SHFT 0
+
+#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52
+#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080
+#define HDMI_REG_TMDS_FREQ_0_SHFT 7
+#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F
+#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0
+
+#define HDMI_REG_RST_CTRLS_ADDR 0x5A
+#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008
+#define HDMI_HDCP_REPT_EDID_RST_SHFT 3
+
+#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E
+#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008
+#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3
/* HDMI Repeater Map Registers */
-#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74
-#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001
-#define HDMI_MAN_EDID_A_ENABLE_SHFT 0
+#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74
+#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001
+#define HDMI_MAN_EDID_A_ENABLE_SHFT 0
+
+#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76
+#define HDMI_EDID_A_ENABLE_BMSK 0x0001
+#define HDMI_EDID_A_ENABLE_SHFT 0
+
+/* CEC Map Registers */
+#define CEC_REG_LOG_ADDR_MASK_ADDR 0x27
+#define CEC_REG_LOG_ADDR_MASK2_BMSK 0x0040
+#define CEC_REG_LOG_ADDR_MASK2_SHFT 6
+#define CEC_REG_LOG_ADDR_MASK1_BMSK 0x0020
+#define CEC_REG_LOG_ADDR_MASK1_SHFT 5
+#define CEC_REG_LOG_ADDR_MASK0_BMSK 0x0010
+#define CEC_REG_LOG_ADDR_MASK0_SHFT 4
+#define CEC_REG_ERROR_REPORT_MODE_BMSK 0x0008
+#define CEC_REG_ERROR_REPORT_MODE_SHFT 3
+#define CEC_REG_ERROR_REPORT_DET_BMSK 0x0004
+#define CEC_REG_ERROR_REPORT_DET_SHFT 2
+#define CEC_REG_FORCE_NACK_BMSK 0x0002
+#define CEC_REG_FORCE_NACK_SHFT 1
+#define CEC_REG_FORCE_IGNORE_BMSK 0x0001
+#define CEC_REG_FORCE_IGNORE_SHFT 0
+
+#define CEC_REG_LOGICAL_ADDRESS0_1_ADDR 0x28
+#define CEC_REG_LOGICAL_ADDRESS1_BMSK 0x00F0
+#define CEC_REG_LOGICAL_ADDRESS1_SHFT 4
+#define CEC_REG_LOGICAL_ADDRESS0_BMSK 0x000F
+#define CEC_REG_LOGICAL_ADDRESS0_SHFT 0
+
+#define CEC_REG_LOGICAL_ADDRESS2_ADDR 0x29
+#define CEC_REG_LOGICAL_ADDRESS2_BMSK 0x000F
+#define CEC_REG_LOGICAL_ADDRESS2_SHFT 0
+
+#define CEC_REG_CEC_POWER_UP_ADDR 0x2A
+#define CEC_REG_CEC_POWER_UP_BMSK 0x0001
+#define CEC_REG_CEC_POWER_UP_SHFT 0
+
+#define CEC_REG_CLR_RX_RDY_SFT_RST_ADDR 0x2C
+#define CEC_REG_CEC_SOFT_RESET_BMSK 0x0001
+#define CEC_REG_CEC_SOFT_RESET_SHFT 0
-#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76
-#define HDMI_EDID_A_ENABLE_BMSK 0x0001
-#define HDMI_EDID_A_ENABLE_SHFT 0
/* CP Map Registers */
-#define CP_REG_CONTRAST 0x3A
-#define CP_REG_SATURATION 0x3B
-#define CP_REG_BRIGHTNESS 0x3C
-#define CP_REG_HUE 0x3D
-#define CP_REG_VID_ADJ 0x3E
-#define CP_CTR_VID_ADJ_EN 0x80
-#define CP_REG_STDI_CH_ADDR 0xB1
-#define CP_STDI_DVALID_CH1_BMSK 0x0080
-#define CP_STDI_DVALID_CH1_SHFT 7
+#define CP_REG_CONTRAST 0x3A
+#define CP_REG_SATURATION 0x3B
+#define CP_REG_BRIGHTNESS 0x3C
+#define CP_REG_HUE 0x3D
+#define CP_REG_VID_ADJ 0x3E
+#define CP_CTR_VID_ADJ_EN 0x80
+#define CP_REG_STDI_CH_ADDR 0xB1
+#define CP_STDI_DVALID_CH1_BMSK 0x0080
+#define CP_STDI_DVALID_CH1_SHFT 7
+
+/* SDP Main Map */
+#define SDP_RW_MAP_REG 0x0e
+
+/* SDP MAP 1 Registers */
+#define SDP_RW_LOCK_UNLOCK_CLR_ADDR 0x43
+#define SDP_RW_LOCK_UNLOCK_MASK_ADDR 0x44
/* SDP R/O Main Map Registers */
-#define SDP_RO_MAIN_STATUS1_ADDR 0x10
-#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080
-#define SDP_RO_MAIN_COL_KILL_SHFT 7
-#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070
-#define SDP_RO_MAIN_AD_RESULT_SHFT 4
-#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008
-#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3
-#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004
-#define SDP_RO_MAIN_FSC_LOCK_SHFT 2
-#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002
-#define SDP_RO_MAIN_LOST_LOCK_SHFT 1
-#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001
-#define SDP_RO_MAIN_IN_LOCK_SHFT 0
+#define SDP_RO_MAIN_STATUS1_ADDR 0x10
+#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080
+#define SDP_RO_MAIN_COL_KILL_SHFT 7
+#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070
+#define SDP_RO_MAIN_AD_RESULT_SHFT 4
+#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008
+#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3
+#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004
+#define SDP_RO_MAIN_FSC_LOCK_SHFT 2
+#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002
+#define SDP_RO_MAIN_LOST_LOCK_SHFT 1
+#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001
+#define SDP_RO_MAIN_IN_LOCK_SHFT 0
+
/*
* CSI Map Registers
*/
-#define CSI_REG_TX_CFG1_ADDR 0x00
-#define CSI_CTRL_TX_PWRDN_BMSK 0x0080
-#define CSI_CTRL_TX_PWRDN_SHFT 7
-#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020
-#define CSI_CTRL_AUTO_PARAMS_SHFT 5
-#define CSI_CTRL_NUM_LANES_BMSK 0x0007
-#define CSI_CTRL_NUM_LANES_SHFT 0
-
-#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0
-#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001
-#define CSI_CTRL_DPHY_PWDN_SHFT 0
+#define CSI_REG_TX_CFG1_ADDR 0x00
+#define CSI_CTRL_TX_PWRDN_BMSK 0x0080
+#define CSI_CTRL_TX_PWRDN_SHFT 7
+#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020
+#define CSI_CTRL_AUTO_PARAMS_SHFT 5
+#define CSI_CTRL_NUM_LANES_BMSK 0x0007
+#define CSI_CTRL_NUM_LANES_SHFT 0
+
+#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0
+#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001
+#define CSI_CTRL_DPHY_PWDN_SHFT 0
enum adv7481_adresult {
- AD_NTSM_M_J = 0x0,
- AD_NTSC_4_43 = 0x1,
- AD_PAL_M = 0x2,
- AD_PAL_60 = 0x3,
- AD_PAL_B_G = 0x4,
- AD_SECAM = 0x5,
- AD_PAL_COMB_N = 0x6,
- AD_SECAM_525 = 0x7,
+ AD_NTSM_M_J = 0x0,
+ AD_NTSC_4_43 = 0x1,
+ AD_PAL_M = 0x2,
+ AD_PAL_60 = 0x3,
+ AD_PAL_B_G = 0x4,
+ AD_SECAM = 0x5,
+ AD_PAL_COMB_N = 0x6,
+ AD_SECAM_525 = 0x7,
};
enum adv7481_color_depth {
- CD_8BIT = 0x0,
- CD_10BIT = 0x1,
- CD_12BIT = 0x2,
- CD_16BIT = 0x3,
+ CD_8BIT = 0x0,
+ CD_10BIT = 0x1,
+ CD_12BIT = 0x2,
+ CD_16BIT = 0x3,
};
enum adv7481_intrq_dur_sel {
- AD_4_XTAL_PER = 0x0,
- AD_16_XTAL_PER = 0x1,
- AD_64_XTAL_PER = 0x2,
- AD_ACTIVE_UNTIL_CLR = 0x3,
+ AD_4_XTAL_PER = 0x0,
+ AD_16_XTAL_PER = 0x1,
+ AD_64_XTAL_PER = 0x2,
+ AD_ACTIVE_UNTIL_CLR = 0x3,
};
enum adv7481_intrq_op_sel {
- AD_OP_OPEN_DRAIN = 0x0,
- AD_OP_DRIVE_LOW = 0x1,
- AD_OP_DRIVE_HIGH = 0x2,
- AD_OP_DISABLED = 0x3,
+ AD_OP_OPEN_DRAIN = 0x0,
+ AD_OP_DRIVE_LOW = 0x1,
+ AD_OP_DRIVE_HIGH = 0x2,
+ AD_OP_DISABLED = 0x3,
};
enum adv7481_drv_llc_pad {
- AD_LLC_PAD_NOT_USED = 0x0,
- AD_MIN_DRIVE_STRNGTH = 0x1,
- AD_MID_DRIVE_STRNGTH = 0x2,
- AD_MAX_DRIVE_STRNGTH = 0x3,
+ AD_LLC_PAD_NOT_USED = 0x0,
+ AD_MIN_DRIVE_STRNGTH = 0x1,
+ AD_MID_DRIVE_STRNGTH = 0x2,
+ AD_MAX_DRIVE_STRNGTH = 0x3,
};
#endif
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index 03d1b3c22d61..6515e3d6ecbc 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -1360,6 +1360,7 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
{
uint16_t first_pixel, last_pixel, first_line, last_line;
struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ struct msm_vfe_testgen_cfg *testgen_cfg = &pix_cfg->testgen_cfg;
uint32_t val, subsample_period, subsample_pattern;
uint32_t irq_sub_period = 32;
uint32_t frame_sub_period = 32;
@@ -1383,8 +1384,15 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
subsample_period = camif_cfg->subsample_cfg.irq_subsample_period;
subsample_pattern = camif_cfg->subsample_cfg.irq_subsample_pattern;
- msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 |
- (camif_cfg->pixels_per_line - 1), vfe_dev->vfe_base + 0x484);
+ if (pix_cfg->input_mux == TESTGEN)
+ msm_camera_io_w((testgen_cfg->lines_per_frame - 1) << 16 |
+ (testgen_cfg->pixels_per_line - 1),
+ vfe_dev->vfe_base + 0x484);
+ else
+ msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 |
+ (camif_cfg->pixels_per_line - 1),
+ vfe_dev->vfe_base + 0x484);
+
if (bus_sub_en) {
val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C);
val &= 0xFFFFFFDF;
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 7e74f2f10c8c..e2f068a21c28 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
@@ -2498,7 +2498,8 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
intf = SRC_TO_INTF(stream_info->stream_src);
vfe_dev->axi_data.src_info[intf].lpm =
ab_ib_vote->lpm_mode;
- if (stream_info->lpm_mode) {
+ if (stream_info->lpm_mode ||
+ stream_info->state == INACTIVE) {
spin_unlock_irqrestore(&stream_info->lock,
flags);
continue;
@@ -2518,7 +2519,8 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
intf = SRC_TO_INTF(stream_info->stream_src);
vfe_dev->axi_data.src_info[intf].lpm =
ab_ib_vote->lpm_mode;
- if (stream_info->lpm_mode == 0) {
+ if (stream_info->lpm_mode == 0 ||
+ stream_info->state == INACTIVE) {
spin_unlock_irqrestore(&stream_info->lock,
flags);
continue;
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 ee695bf5dfd9..f0831e64f250 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
@@ -267,7 +267,9 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev,
int result = 0;
memset(&buf_event, 0, sizeof(struct msm_isp_event_data));
- buf_event.timestamp = ts->buf_time;
+ buf_event.timestamp = ts->event_time;
+ buf_event.mono_timestamp = ts->buf_time;
+
buf_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
pingpong_status = vfe_dev->hw_info->
vfe_ops.stats_ops.get_pingpong_status(vfe_dev);
@@ -1263,7 +1265,7 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
&update_cmd->update_info[i];
/*check array reference bounds*/
if (STATS_IDX(update_info->stream_handle)
- > vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
pr_err("%s: stats idx %d out of bound!", __func__,
STATS_IDX(update_info->stream_handle));
return -EINVAL;
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 f19e6dd1cb01..d30d8022b7ab 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
@@ -479,8 +479,12 @@ static int msm_isp_cfg_pix(struct vfe_device *vfe_dev,
if (input_cfg->d.pix_cfg.input_mux == CAMIF ||
input_cfg->d.pix_cfg.input_mux == TESTGEN) {
- vfe_dev->axi_data.src_info[VFE_PIX_0].width =
- input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
+ if (input_cfg->d.pix_cfg.input_mux == CAMIF)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
+ if (input_cfg->d.pix_cfg.input_mux == TESTGEN)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.testgen_cfg.pixels_per_line;
if (input_cfg->d.pix_cfg.camif_cfg.subsample_cfg.
sof_counter_step > 0) {
vfe_dev->axi_data.src_info[VFE_PIX_0].
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index ab7d4e86dcac..ab981f762dd2 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -1021,6 +1021,11 @@ static void msm_ispif_config_stereo(struct ispif_device *ispif,
for (i = 0; i < params->num; i++) {
vfe_intf = params->entries[i].vfe_intf;
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type %d\n", __func__,
+ vfe_intf);
+ return;
+ }
if (params->entries[i].intftype == PIX0 &&
params->stereo_enable &&
params->right_entries[i].csid < CSID_MAX &&
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index f95cc37f5c2c..9cb7d5299ef8 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -32,7 +32,6 @@
#include "cam_hw_ops.h"
#include <media/msmb_generic_buf_mgr.h>
-
static struct v4l2_device *msm_v4l2_dev;
static struct list_head ordered_sd_list;
@@ -149,7 +148,7 @@ typedef int (*msm_queue_find_func)(void *d1, void *d2);
#define msm_queue_find(queue, type, member, func, data) ({\
unsigned long flags; \
struct msm_queue_head *__q = (queue); \
- type *node = 0; \
+ type *node = NULL; \
typeof(node) __ret = NULL; \
msm_queue_find_func __f = (func); \
spin_lock_irqsave(&__q->lock, flags); \
@@ -279,22 +278,47 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
struct msm_session *session = NULL;
struct msm_stream *stream = NULL;
unsigned long flags;
+ int try_count = 0;
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
+
if (!session)
return;
- stream = msm_queue_find(&session->stream_q, struct msm_stream,
- list, __msm_queue_find_stream, &stream_id);
- if (!stream)
- return;
- spin_lock_irqsave(&(session->stream_q.lock), flags);
- list_del_init(&stream->list);
- session->stream_q.len--;
- kfree(stream);
- stream = NULL;
- spin_unlock_irqrestore(&(session->stream_q.lock), flags);
+ while (1) {
+
+ if (try_count > 5) {
+ pr_err("%s : not able to delete stream %d\n",
+ __func__, __LINE__);
+ break;
+ }
+
+ write_lock(&session->stream_rwlock);
+ try_count++;
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+
+ if (!stream) {
+ write_unlock(&session->stream_rwlock);
+ return;
+ }
+
+ if (msm_vb2_get_stream_state(stream) != 1) {
+ write_unlock(&session->stream_rwlock);
+ continue;
+ }
+
+ spin_lock_irqsave(&(session->stream_q.lock), flags);
+ list_del_init(&stream->list);
+ session->stream_q.len--;
+ kfree(stream);
+ stream = NULL;
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags);
+ write_unlock(&session->stream_rwlock);
+ break;
+ }
+
}
EXPORT_SYMBOL(msm_delete_stream);
@@ -444,6 +468,7 @@ int msm_create_session(unsigned int session_id, struct video_device *vdev)
mutex_init(&session->lock);
mutex_init(&session->lock_q);
mutex_init(&session->close_lock);
+ rwlock_init(&session->stream_rwlock);
if (gpu_limit) {
session->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0);
@@ -1048,17 +1073,25 @@ static struct v4l2_file_operations msm_fops = {
#endif
};
-struct msm_stream *msm_get_stream(unsigned int session_id,
- unsigned int stream_id)
+struct msm_session *msm_get_session(unsigned int session_id)
{
struct msm_session *session;
- struct msm_stream *stream;
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
if (!session)
return ERR_PTR(-EINVAL);
+ return session;
+}
+EXPORT_SYMBOL(msm_get_session);
+
+
+struct msm_stream *msm_get_stream(struct msm_session *session,
+ unsigned int stream_id)
+{
+ struct msm_stream *stream;
+
stream = msm_queue_find(&session->stream_q, struct msm_stream,
list, __msm_queue_find_stream, &stream_id);
@@ -1115,6 +1148,34 @@ struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q)
}
EXPORT_SYMBOL(msm_get_stream_from_vb2q);
+struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+ unsigned long flags1;
+ unsigned long flags2;
+
+ spin_lock_irqsave(&msm_session_q->lock, flags1);
+ list_for_each_entry(session, &(msm_session_q->list), list) {
+ spin_lock_irqsave(&(session->stream_q.lock), flags2);
+ list_for_each_entry(
+ stream, &(session->stream_q.list), list) {
+ if (stream->vb2_q == q) {
+ spin_unlock_irqrestore
+ (&(session->stream_q.lock), flags2);
+ spin_unlock_irqrestore
+ (&msm_session_q->lock, flags1);
+ return session;
+ }
+ }
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags2);
+ }
+ spin_unlock_irqrestore(&msm_session_q->lock, flags1);
+ return NULL;
+}
+EXPORT_SYMBOL(msm_get_session_from_vb2q);
+
+
#ifdef CONFIG_COMPAT
long msm_copy_camera_private_ioctl_args(unsigned long arg,
struct msm_camera_private_ioctl_arg *k_ioctl,
diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h
index 7474cb119147..dce47bc7249c 100644
--- a/drivers/media/platform/msm/camera_v2/msm.h
+++ b/drivers/media/platform/msm/camera_v2/msm.h
@@ -111,6 +111,7 @@ struct msm_session {
struct mutex lock;
struct mutex lock_q;
struct mutex close_lock;
+ rwlock_t stream_rwlock;
struct kgsl_pwr_limit *sysfs_pwr_limit;
};
@@ -129,11 +130,13 @@ int msm_create_stream(unsigned int session_id,
void msm_delete_stream(unsigned int session_id, unsigned int stream_id);
int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id);
void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id);
-struct msm_stream *msm_get_stream(unsigned int session_id,
+struct msm_session *msm_get_session(unsigned int session_id);
+struct msm_stream *msm_get_stream(struct msm_session *session,
unsigned int stream_id);
struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id,
unsigned int stream_id);
struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q);
+struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q);
struct msm_session *msm_session_find(unsigned int session_id);
#ifdef CONFIG_COMPAT
long msm_copy_camera_private_ioctl_args(unsigned long arg,
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index c779ee46c19a..ba9b4df6bf22 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.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
@@ -44,17 +44,25 @@ static int msm_vb2_queue_setup(struct vb2_queue *q,
int msm_vb2_buf_init(struct vb2_buffer *vb)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct msm_vb2_buffer *msm_vb2_buf;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s: Couldn't find stream\n", __func__);
+ read_unlock(&session->stream_rwlock);
return -EINVAL;
}
msm_vb2_buf = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
msm_vb2_buf->in_freeq = 0;
-
+ read_unlock(&session->stream_rwlock);
return 0;
}
@@ -62,6 +70,7 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb)
{
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -71,21 +80,30 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb)
return;
}
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
spin_lock_irqsave(&stream->stream_lock, flags);
list_add_tail(&msm_vb2->list, &stream->queued_list);
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
}
static void msm_vb2_buf_finish(struct vb2_buffer *vb)
{
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct msm_vb2_buffer *msm_vb2_entry, *temp;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -96,9 +114,16 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
return;
}
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -111,6 +136,7 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
}
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -118,12 +144,20 @@ static void msm_vb2_stop_stream(struct vb2_queue *q)
{
struct msm_vb2_buffer *msm_vb2, *temp;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct vb2_v4l2_buffer *vb2_v4l2_buf;
+ session = msm_get_session_from_vb2q(q);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(q);
if (!stream) {
pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -143,7 +177,27 @@ static void msm_vb2_stop_stream(struct vb2_queue *q)
msm_vb2->in_freeq = 0;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
+}
+
+int msm_vb2_get_stream_state(struct msm_stream *stream)
+{
+ struct msm_vb2_buffer *msm_vb2, *temp;
+ unsigned long flags;
+ int rc = 1;
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ list_for_each_entry_safe(msm_vb2, temp, &(stream->queued_list), list) {
+ if (msm_vb2->in_freeq != 0) {
+ rc = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ return rc;
}
+EXPORT_SYMBOL(msm_vb2_get_stream_state);
+
static struct vb2_ops msm_vb2_get_q_op = {
.queue_setup = msm_vb2_queue_setup,
@@ -198,14 +252,23 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
unsigned int stream_id)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return NULL;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return NULL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (!stream->vb2_q) {
@@ -228,6 +291,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
vb2_v4l2_buf = NULL;
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return vb2_v4l2_buf;
}
@@ -235,13 +299,23 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
unsigned int stream_id, uint32_t index)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return NULL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
return NULL;
+ }
spin_lock_irqsave(&stream->stream_lock, flags);
@@ -263,6 +337,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
vb2_v4l2_buf = NULL;
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return vb2_v4l2_buf;
}
@@ -270,14 +345,24 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
unsigned int stream_id)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct msm_vb2_buffer *msm_vb2;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
int rc = 0;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return -EINVAL;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (vb) {
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
@@ -305,6 +390,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
rc = -EINVAL;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
@@ -315,12 +401,22 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
unsigned long flags;
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
int rc = 0;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (vb) {
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
@@ -352,6 +448,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
rc = -EINVAL;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
@@ -359,15 +456,24 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
uint32_t index)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
long rc = -EINVAL;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return rc;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (!stream->vb2_q) {
@@ -393,6 +499,7 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
EXPORT_SYMBOL(msm_vb2_return_buf_by_idx);
@@ -402,11 +509,21 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
unsigned long flags;
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
@@ -415,6 +532,7 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
msm_vb2->in_freeq = 0;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
index 53511d5416d7..c65cb58128d9 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.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
@@ -67,5 +67,6 @@ struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void);
int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req_sd);
long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
uint32_t index);
+int msm_vb2_get_stream_state(struct msm_stream *stream);
#endif /*_MSM_VB_H */
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
index c9dfb52861bc..1d30a869d754 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.c
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -490,11 +490,13 @@ bool msm_smem_compare_buffers(void *clt, int fd, void *priv)
}
static int ion_cache_operations(struct smem_client *client,
- struct msm_smem *mem, enum smem_cache_ops cache_op)
+ struct msm_smem *mem, enum smem_cache_ops cache_op,
+ int size)
{
unsigned long ionflag = 0;
int rc = 0;
int msm_cache_ops = 0;
+ int op_size = 0;
if (!mem || !client) {
dprintk(VIDC_ERR, "Invalid params: %pK, %pK\n",
mem, client);
@@ -523,10 +525,15 @@ static int ion_cache_operations(struct smem_client *client,
rc = -EINVAL;
goto cache_op_failed;
}
+ if (size <= 0)
+ op_size = mem->size;
+ else
+ op_size = mem->size < size ? mem->size : size;
+
rc = msm_ion_do_cache_offset_op(client->clnt,
(struct ion_handle *)mem->smem_priv,
0, mem->offset,
- (unsigned long)mem->size, msm_cache_ops);
+ (unsigned long)op_size, msm_cache_ops);
if (rc) {
dprintk(VIDC_ERR,
"cache operation failed %d\n", rc);
@@ -538,7 +545,7 @@ cache_op_failed:
}
int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
- enum smem_cache_ops cache_op)
+ enum smem_cache_ops cache_op, int size)
{
struct smem_client *client = clt;
int rc = 0;
@@ -549,7 +556,7 @@ int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
}
switch (client->mem_type) {
case SMEM_ION:
- rc = ion_cache_operations(client, mem, cache_op);
+ rc = ion_cache_operations(client, mem, cache_op, size);
if (rc)
dprintk(VIDC_ERR,
"Failed cache operations: %d\n", rc);
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index a8dc1d010d62..c0271c757020 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -766,7 +766,6 @@ 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;
@@ -778,7 +777,6 @@ 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_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 8ac84ece2c2a..0764a18a7993 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -1781,8 +1781,10 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state == VIDC_CORE_INVALID ||
- inst->core->state == VIDC_CORE_UNINIT)
- return -EINVAL;
+ inst->core->state == VIDC_CORE_UNINIT) {
+ rc = -EINVAL;
+ goto stream_start_failed;
+ }
hdev = inst->core->device;
dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n",
@@ -2233,6 +2235,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
struct hal_enable_picture enable_picture;
struct hal_enable hal_property;
enum hal_property property_id = 0;
+ enum hal_video_codec codec;
u32 property_val = 0;
void *pdata = NULL;
struct hfi_device *hdev;
@@ -2287,12 +2290,23 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE:
property_id = HAL_PARAM_VDEC_PICTURE_TYPE_DECODE;
if (ctrl->val ==
- V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON)
+ V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON) {
enable_picture.picture_type = HAL_PICTURE_I;
- else
- enable_picture.picture_type = HAL_PICTURE_I |
- HAL_PICTURE_P | HAL_PICTURE_B |
- HAL_PICTURE_IDR;
+ } else {
+ codec = get_hal_codec(inst->fmts[OUTPUT_PORT].fourcc);
+ if (codec == HAL_VIDEO_CODEC_H264) {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B |
+ HAL_PICTURE_IDR;
+ } else if (codec == HAL_VIDEO_CODEC_HEVC) {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B |
+ HAL_PICTURE_IDR | HAL_PICTURE_CRA;
+ } else {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B;
+ }
+ }
pdata = &enable_picture;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO:
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index cdf91dd80ed3..a4cd4e34c7d0 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1908,8 +1908,10 @@ static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count)
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state == VIDC_CORE_INVALID ||
- inst->core->state == VIDC_CORE_UNINIT)
- return -EINVAL;
+ inst->core->state == VIDC_CORE_UNINIT) {
+ rc = -EINVAL;
+ goto stream_start_failed;
+ }
dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n",
q->type, inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index f09c28fed6d2..3e4be4418f80 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -670,10 +670,11 @@ int qbuf_dynamic_buf(struct msm_vidc_inst *inst,
}
int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
- struct buffer_info *binfo)
+ struct buffer_info *binfo, struct v4l2_buffer *b)
{
int i = 0;
int rc = 0;
+ int size = -1;
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst);
@@ -686,23 +687,34 @@ int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
return -EINVAL;
}
- for (i = 0; i < binfo->num_planes; i++) {
- if (binfo->handle[i]) {
- struct msm_smem smem = *binfo->handle[i];
-
- smem.offset = (unsigned int)(binfo->buff_off[i]);
- smem.size = binfo->size[i];
- rc = msm_comm_smem_cache_operations(inst,
- &smem, SMEM_CACHE_INVALIDATE);
- if (rc) {
- dprintk(VIDC_ERR,
- "%s: Failed to clean caches: %d\n",
- __func__, rc);
- return -EINVAL;
- }
- } else
- dprintk(VIDC_DBG, "%s: NULL handle for plane %d\n",
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ for (i = 0; i < binfo->num_planes; i++) {
+ if (binfo->handle[i]) {
+ struct msm_smem smem = *binfo->handle[i];
+
+ if (inst->session_type == MSM_VIDC_ENCODER &&
+ !i)
+ size = b->m.planes[i].bytesused;
+ else
+ size = -1;
+
+ smem.offset =
+ (unsigned int)(binfo->buff_off[i]);
+ smem.size = binfo->size[i];
+ rc = msm_comm_smem_cache_operations(inst,
+ &smem, SMEM_CACHE_INVALIDATE,
+ size);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: Failed to clean caches: %d\n",
+ __func__, rc);
+ return -EINVAL;
+ }
+ } else
+ dprintk(VIDC_DBG,
+ "%s: NULL handle for plane %d\n",
__func__, i);
+ }
}
return 0;
}
@@ -858,6 +870,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
int plane = 0;
int rc = 0;
int i;
+ int size = -1;
if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst))
return -EINVAL;
@@ -905,7 +918,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
V4L2_PIX_FMT_HEVC_HYBRID && binfo->handle[i] &&
b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
rc = msm_comm_smem_cache_operations(inst,
- binfo->handle[i], SMEM_CACHE_INVALIDATE);
+ binfo->handle[i], SMEM_CACHE_INVALIDATE, -1);
if (rc) {
dprintk(VIDC_ERR,
"Failed to inv caches: %d\n", rc);
@@ -915,8 +928,13 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
if (binfo->handle[i] &&
(b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) {
+ if (inst->session_type == MSM_VIDC_DECODER && !i)
+ size = b->m.planes[i].bytesused;
+ else
+ size = -1;
rc = msm_comm_smem_cache_operations(inst,
- binfo->handle[i], SMEM_CACHE_CLEAN);
+ binfo->handle[i], SMEM_CACHE_CLEAN,
+ size);
if (rc) {
dprintk(VIDC_ERR,
"Failed to clean caches: %d\n", rc);
@@ -985,7 +1003,7 @@ int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b)
return -EINVAL;
}
- rc = output_buffer_cache_invalidate(inst, buffer_info);
+ rc = output_buffer_cache_invalidate(inst, buffer_info, b);
if (rc)
return rc;
@@ -1347,8 +1365,6 @@ static void cleanup_instance(struct msm_vidc_inst *inst)
"Failed to release output buffers\n");
}
- debugfs_remove_recursive(inst->debugfs_root);
-
mutex_lock(&inst->pending_getpropq.lock);
if (!list_empty(&inst->pending_getpropq.list)) {
dprintk(VIDC_ERR,
@@ -1390,6 +1406,8 @@ int msm_vidc_destroy(struct msm_vidc_inst *inst)
mutex_destroy(&inst->bufq[OUTPUT_PORT].lock);
mutex_destroy(&inst->lock);
+ msm_vidc_debugfs_deinit_inst(inst);
+
pr_info(VIDC_DBG_TAG "Closed video instance: %pK\n",
VIDC_MSG_PRIO2STRING(VIDC_INFO), inst);
kfree(inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 7b28e80979f2..c9a871bae25b 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -3174,7 +3174,7 @@ static int set_output_buffers(struct msm_vidc_inst *inst,
goto err_no_mem;
}
rc = msm_comm_smem_cache_operations(inst,
- handle, SMEM_CACHE_CLEAN);
+ handle, SMEM_CACHE_CLEAN, -1);
if (rc) {
dprintk(VIDC_WARN,
"Failed to clean cache may cause undefined behavior\n");
@@ -3265,7 +3265,7 @@ static int set_internal_buf_on_fw(struct msm_vidc_inst *inst,
hdev = inst->core->device;
rc = msm_comm_smem_cache_operations(inst,
- handle, SMEM_CACHE_CLEAN);
+ handle, SMEM_CACHE_CLEAN, -1);
if (rc) {
dprintk(VIDC_WARN,
"Failed to clean cache. Undefined behavior\n");
@@ -4524,10 +4524,15 @@ static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst)
struct vb2_buffer *vb = container_of(ptr,
struct vb2_buffer, queued_entry);
- vb->planes[0].bytesused = 0;
- vb->planes[0].data_offset = 0;
-
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ if (vb->state == VB2_BUF_STATE_ACTIVE) {
+ vb->planes[0].bytesused = 0;
+ vb->planes[0].data_offset = 0;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ } else {
+ dprintk(VIDC_WARN,
+ "%s VB is in state %d not in ACTIVE state\n"
+ , __func__, vb->state);
+ }
}
mutex_unlock(&inst->bufq[port].lock);
}
@@ -5154,14 +5159,16 @@ void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem)
}
int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst,
- struct msm_smem *mem, enum smem_cache_ops cache_ops)
+ struct msm_smem *mem, enum smem_cache_ops cache_ops,
+ int size)
{
if (!inst || !mem) {
dprintk(VIDC_ERR,
"%s: invalid params: %pK %pK\n", __func__, inst, mem);
return -EINVAL;
}
- return msm_smem_cache_operations(inst->mem_client, mem, cache_ops);
+ return msm_smem_cache_operations(inst->mem_client, mem,
+ cache_ops, size);
}
struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index eac7f658eb31..01c1890bf5e1 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.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
@@ -76,7 +76,7 @@ struct msm_smem *msm_comm_smem_alloc(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type, int map_kernel);
void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem);
int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst,
- struct msm_smem *mem, enum smem_cache_ops cache_ops);
+ struct msm_smem *mem, enum smem_cache_ops cache_ops, int size);
struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst,
int fd, u32 offset, enum hal_buffer buffer_type);
enum hal_video_codec get_hal_codec(int fourcc);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index 885e61f8bf01..5c13b6fef3ec 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -38,44 +38,31 @@ 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;
-};
-
-static struct debug_buffer dbg_buf;
-
-#define INIT_DBG_BUF(__buf) ({ \
- __buf.curr = __buf.ptr;\
- __buf.filled_size = 0; \
-})
-
#define DYNAMIC_BUF_OWNER(__binfo) ({ \
atomic_read(&__binfo->ref_count) == 2 ? "video driver" : "firmware";\
})
+struct core_inst_pair {
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst;
+};
+
static int core_info_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
-static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...)
+static u32 write_str(char *buffer,
+ size_t size, const char *fmt, ...)
{
va_list args;
- u32 size;
-
- char *curr = buffer->curr;
- char *end = buffer->ptr + MAX_DBG_BUF_SIZE;
+ u32 len;
va_start(args, fmt);
- size = vscnprintf(curr, end - curr, fmt, args);
+ len = vscnprintf(buffer, size, fmt, args);
va_end(args);
- buffer->curr += size;
- buffer->filled_size += size;
- return size;
+ return len;
}
static ssize_t core_info_read(struct file *file, char __user *buf,
@@ -84,6 +71,7 @@ static ssize_t core_info_read(struct file *file, char __user *buf,
struct msm_vidc_core *core = file->private_data;
struct hfi_device *hdev;
struct hal_fw_info fw_info = { {0} };
+ char *dbuf, *cur, *end;
int i = 0, rc = 0;
ssize_t len = 0;
@@ -91,36 +79,46 @@ static ssize_t core_info_read(struct file *file, char __user *buf,
dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core);
return 0;
}
+
+ dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL);
+ if (!dbuf) {
+ dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__);
+ return -ENOMEM;
+ }
+ cur = dbuf;
+ end = cur + MAX_DBG_BUF_SIZE;
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);
- write_str(&dbg_buf, "===============================\n");
- write_str(&dbg_buf, "Core state: %d\n", core->state);
+ cur += write_str(cur, end - cur, "===============================\n");
+ cur += write_str(cur, end - cur, "CORE %d: %pK\n", core->id, core);
+ cur += write_str(cur, end - cur, "===============================\n");
+ cur += write_str(cur, end - cur, "Core state: %d\n", core->state);
rc = call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, &fw_info);
if (rc) {
dprintk(VIDC_WARN, "Failed to read FW info\n");
goto err_fw_info;
}
- write_str(&dbg_buf, "FW version : %s\n", &fw_info.version);
- write_str(&dbg_buf, "base addr: 0x%x\n", fw_info.base_addr);
- write_str(&dbg_buf, "register_base: 0x%x\n", fw_info.register_base);
- write_str(&dbg_buf, "register_size: %u\n", fw_info.register_size);
- write_str(&dbg_buf, "irq: %u\n", fw_info.irq);
+ cur += write_str(cur, end - cur,
+ "FW version : %s\n", &fw_info.version);
+ cur += write_str(cur, end - cur,
+ "base addr: 0x%x\n", fw_info.base_addr);
+ cur += write_str(cur, end - cur,
+ "register_base: 0x%x\n", fw_info.register_base);
+ cur += write_str(cur, end - cur,
+ "register_size: %u\n", fw_info.register_size);
+ cur += write_str(cur, end - cur, "irq: %u\n", fw_info.irq);
err_fw_info:
for (i = SYS_MSG_START; i < SYS_MSG_END; i++) {
- write_str(&dbg_buf, "completions[%d]: %s\n", i,
+ cur += write_str(cur, end - cur, "completions[%d]: %s\n", i,
completion_done(&core->completions[SYS_MSG_INDEX(i)]) ?
"pending" : "done");
}
len = simple_read_from_buffer(buf, count, ppos,
- dbg_buf.ptr, dbg_buf.filled_size);
+ dbuf, cur - dbuf);
- mutex_unlock(&dbg_buf.lock);
+ kfree(dbuf);
return len;
}
@@ -177,7 +175,6 @@ struct dentry *msm_vidc_debugfs_init_drv(void)
bool ok = false;
struct dentry *dir = NULL;
- mutex_init(&dbg_buf.lock);
dir = debugfs_create_dir("msm_vidc", NULL);
if (IS_ERR_OR_NULL(dir)) {
dir = NULL;
@@ -263,12 +260,15 @@ failed_create_dir:
static int inst_info_open(struct inode *inode, struct file *file)
{
+ dprintk(VIDC_INFO, "Open inode ptr: %pK\n", inode->i_private);
file->private_data = inode->i_private;
return 0;
}
-static int publish_unreleased_reference(struct msm_vidc_inst *inst)
+static int publish_unreleased_reference(struct msm_vidc_inst *inst,
+ char **dbuf, char *end)
{
+ char *cur = *dbuf;
struct buffer_info *temp = NULL;
if (!inst) {
@@ -277,130 +277,228 @@ static int publish_unreleased_reference(struct msm_vidc_inst *inst)
}
if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) {
- write_str(&dbg_buf, "Pending buffer references:\n");
+ cur += write_str(cur, end - cur, "Pending buffer references\n");
mutex_lock(&inst->registeredbufs.lock);
list_for_each_entry(temp, &inst->registeredbufs.list, list) {
if (temp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
!temp->inactive && atomic_read(&temp->ref_count)) {
- write_str(&dbg_buf,
- "\tpending buffer: %#lx fd[0] = %d ref_count = %d held by: %s\n",
- temp->device_addr[0],
- temp->fd[0],
- atomic_read(&temp->ref_count),
- DYNAMIC_BUF_OWNER(temp));
+ cur += write_str(cur, end - cur,
+ "\tpending buffer: %#lx fd[0] = %d ref_count = %d held by: %s\n",
+ temp->device_addr[0],
+ temp->fd[0],
+ atomic_read(&temp->ref_count),
+ DYNAMIC_BUF_OWNER(temp));
}
}
mutex_unlock(&inst->registeredbufs.lock);
}
+
+ *dbuf = cur;
return 0;
}
+static void put_inst_helper(struct kref *kref)
+{
+ struct msm_vidc_inst *inst = container_of(kref,
+ struct msm_vidc_inst, kref);
+
+ msm_vidc_destroy(inst);
+}
+
static ssize_t inst_info_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- struct msm_vidc_inst *inst = file->private_data;
+ struct core_inst_pair *idata = file->private_data;
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst, *temp = NULL;
+ char *dbuf, *cur, *end;
int i, j;
ssize_t len = 0;
+ if (!idata || !idata->core || !idata->inst) {
+ dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
+ return 0;
+ }
+
+ core = idata->core;
+ inst = idata->inst;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(temp, &core->instances, list) {
+ if (temp == inst)
+ break;
+ }
+ inst = ((temp == inst) && kref_get_unless_zero(&inst->kref)) ?
+ inst : NULL;
+ mutex_unlock(&core->lock);
+
if (!inst) {
- dprintk(VIDC_ERR, "Invalid params, inst %pK\n", inst);
+ dprintk(VIDC_ERR, "%s: Instance has become obsolete", __func__);
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,
+ dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL);
+ if (!dbuf) {
+ dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__);
+ len = -ENOMEM;
+ goto failed_alloc;
+ }
+ cur = dbuf;
+ end = cur + MAX_DBG_BUF_SIZE;
+
+ cur += write_str(cur, end - cur, "==============================\n");
+ cur += write_str(cur, end - cur, "INSTANCE: %pK (%s)\n", inst,
inst->session_type == MSM_VIDC_ENCODER ? "Encoder" : "Decoder");
- write_str(&dbg_buf, "===============================\n");
- write_str(&dbg_buf, "core: %pK\n", inst->core);
- write_str(&dbg_buf, "height: %d\n", inst->prop.height[CAPTURE_PORT]);
- write_str(&dbg_buf, "width: %d\n", inst->prop.width[CAPTURE_PORT]);
- write_str(&dbg_buf, "fps: %d\n", inst->prop.fps);
- write_str(&dbg_buf, "state: %d\n", inst->state);
- write_str(&dbg_buf, "secure: %d\n", !!(inst->flags & VIDC_SECURE));
- write_str(&dbg_buf, "-----------Formats-------------\n");
+ cur += write_str(cur, end - cur, "==============================\n");
+ cur += write_str(cur, end - cur, "core: %pK\n", inst->core);
+ cur += write_str(cur, end - cur, "height: %d\n",
+ inst->prop.height[CAPTURE_PORT]);
+ cur += write_str(cur, end - cur, "width: %d\n",
+ inst->prop.width[CAPTURE_PORT]);
+ cur += write_str(cur, end - cur, "fps: %d\n", inst->prop.fps);
+ cur += write_str(cur, end - cur, "state: %d\n", inst->state);
+ cur += write_str(cur, end - cur, "secure: %d\n",
+ !!(inst->flags & VIDC_SECURE));
+ cur += write_str(cur, end - cur, "-----------Formats-------------\n");
for (i = 0; i < MAX_PORT_NUM; i++) {
- write_str(&dbg_buf, "capability: %s\n", i == OUTPUT_PORT ?
+ cur += write_str(cur, end - cur, "capability: %s\n",
+ i == OUTPUT_PORT ? "Output" : "Capture");
+ cur += write_str(cur, end - cur, "name : %s\n",
+ inst->fmts[i].name);
+ cur += write_str(cur, end - cur, "planes : %d\n",
+ inst->prop.num_planes[i]);
+ cur += write_str(cur, end - cur,
+ "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ?
"Output" : "Capture");
- write_str(&dbg_buf, "name : %s\n", inst->fmts[i].name);
- write_str(&dbg_buf, "planes : %d\n", inst->prop.num_planes[i]);
- write_str(
- &dbg_buf, "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ?
- "Output" : "Capture");
switch (inst->buffer_mode_set[i]) {
case HAL_BUFFER_MODE_STATIC:
- write_str(&dbg_buf, "buffer mode : %s\n", "static");
+ cur += write_str(cur, end - cur,
+ "buffer mode : %s\n", "static");
break;
case HAL_BUFFER_MODE_RING:
- write_str(&dbg_buf, "buffer mode : %s\n", "ring");
+ cur += write_str(cur, end - cur,
+ "buffer mode : %s\n", "ring");
break;
case HAL_BUFFER_MODE_DYNAMIC:
- write_str(&dbg_buf, "buffer mode : %s\n", "dynamic");
+ cur += write_str(cur, end - cur,
+ "buffer mode : %s\n", "dynamic");
break;
default:
- write_str(&dbg_buf, "buffer mode : unsupported\n");
+ cur += write_str(cur, end - cur,
+ "buffer mode : unsupported\n");
}
- write_str(&dbg_buf, "count: %u\n",
+ cur += write_str(cur, end - cur, "count: %u\n",
inst->bufq[i].vb2_bufq.num_buffers);
for (j = 0; j < inst->prop.num_planes[i]; j++)
- write_str(&dbg_buf, "size for plane %d: %u\n", j,
+ cur += write_str(cur, end - cur,
+ "size for plane %d: %u\n", j,
inst->bufq[i].vb2_bufq.plane_sizes[j]);
if (i < MAX_PORT_NUM - 1)
- write_str(&dbg_buf, "\n");
+ cur += write_str(cur, end - cur, "\n");
}
- write_str(&dbg_buf, "-------------------------------\n");
+ cur += write_str(cur, end - cur, "-------------------------------\n");
for (i = SESSION_MSG_START; i < SESSION_MSG_END; i++) {
- write_str(&dbg_buf, "completions[%d]: %s\n", i,
+ cur += write_str(cur, end - cur, "completions[%d]: %s\n", i,
completion_done(&inst->completions[SESSION_MSG_INDEX(i)]) ?
"pending" : "done");
}
- write_str(&dbg_buf, "ETB Count: %d\n", inst->count.etb);
- write_str(&dbg_buf, "EBD Count: %d\n", inst->count.ebd);
- write_str(&dbg_buf, "FTB Count: %d\n", inst->count.ftb);
- write_str(&dbg_buf, "FBD Count: %d\n", inst->count.fbd);
-
- publish_unreleased_reference(inst);
+ cur += write_str(cur, end - cur, "ETB Count: %d\n", inst->count.etb);
+ cur += write_str(cur, end - cur, "EBD Count: %d\n", inst->count.ebd);
+ cur += write_str(cur, end - cur, "FTB Count: %d\n", inst->count.ftb);
+ cur += write_str(cur, end - cur, "FBD Count: %d\n", inst->count.fbd);
+ publish_unreleased_reference(inst, &cur, end);
len = simple_read_from_buffer(buf, count, ppos,
- dbg_buf.ptr, dbg_buf.filled_size);
- mutex_unlock(&dbg_buf.lock);
+ dbuf, cur - dbuf);
+
+ kfree(dbuf);
+failed_alloc:
+ kref_put(&inst->kref, put_inst_helper);
return len;
}
+static int inst_info_release(struct inode *inode, struct file *file)
+{
+ dprintk(VIDC_INFO, "Release inode ptr: %pK\n", inode->i_private);
+ file->private_data = NULL;
+ return 0;
+}
+
static const struct file_operations inst_info_fops = {
.open = inst_info_open,
.read = inst_info_read,
+ .release = inst_info_release,
};
struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
struct dentry *parent)
{
- struct dentry *dir = NULL;
+ struct dentry *dir = NULL, *info = NULL;
char debugfs_name[MAX_DEBUGFS_NAME];
+ struct core_inst_pair *idata = NULL;
+
if (!inst) {
dprintk(VIDC_ERR, "Invalid params, inst: %pK\n", inst);
- goto failed_create_dir;
+ goto exit;
}
snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst);
+
+ idata = kzalloc(sizeof(struct core_inst_pair), GFP_KERNEL);
+ if (!idata) {
+ dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__);
+ goto exit;
+ }
+
+ idata->core = inst->core;
+ idata->inst = inst;
+
dir = debugfs_create_dir(debugfs_name, parent);
if (!dir) {
dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n");
goto failed_create_dir;
}
- if (!debugfs_create_file("info", S_IRUGO, dir, inst, &inst_info_fops)) {
+
+ info = debugfs_create_file("info", S_IRUGO, dir,
+ idata, &inst_info_fops);
+ if (!info) {
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
- goto failed_create_dir;
+ goto failed_create_file;
}
+
+ dir->d_inode->i_private = info->d_inode->i_private;
inst->debug.pdata[FRAME_PROCESSING].sampling = true;
+ return dir;
+
+failed_create_file:
+ debugfs_remove_recursive(dir);
+ dir = NULL;
failed_create_dir:
+ kfree(idata);
+exit:
return dir;
}
+void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst)
+{
+ struct dentry *dentry = NULL;
+
+ if (!inst || !inst->debugfs_root)
+ return;
+
+ dentry = inst->debugfs_root;
+ if (dentry->d_inode) {
+ dprintk(VIDC_INFO, "Destroy %pK\n", dentry->d_inode->i_private);
+ kfree(dentry->d_inode->i_private);
+ dentry->d_inode->i_private = NULL;
+ }
+ debugfs_remove_recursive(dentry);
+ inst->debugfs_root = NULL;
+}
+
void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
enum msm_vidc_debugfs_event e)
{
@@ -450,8 +548,3 @@ 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 853ce4b89f2b..95b2a6d60936 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
@@ -124,9 +124,9 @@ struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
struct dentry *parent);
struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
struct dentry *parent);
+void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst);
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/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 690a61f4824f..4cb900bbca10 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -355,7 +355,7 @@ struct buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list,
int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo);
int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo);
int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
- struct buffer_info *binfo);
+ struct buffer_info *binfo, struct v4l2_buffer *b);
int qbuf_dynamic_buf(struct msm_vidc_inst *inst,
struct buffer_info *binfo);
int unmap_and_deregister_buf(struct msm_vidc_inst *inst,
@@ -369,7 +369,7 @@ struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags,
void msm_smem_free(void *clt, struct msm_smem *mem);
void msm_smem_delete_client(void *clt);
int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
- enum smem_cache_ops);
+ enum smem_cache_ops, int size);
struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
enum hal_buffer buffer_type);
struct context_bank_info *msm_smem_get_context_bank(void *clt,
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index bc72c4a56c91..52b56f615da9 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -582,7 +582,7 @@ static int __smem_alloc(struct venus_hfi_device *dev,
dprintk(VIDC_DBG, "__smem_alloc: ptr = %pK, size = %d\n",
alloc->kvaddr, size);
rc = msm_smem_cache_operations(dev->hal_client, alloc,
- SMEM_CACHE_CLEAN);
+ SMEM_CACHE_CLEAN, -1);
if (rc) {
dprintk(VIDC_WARN, "Failed to clean cache\n");
dprintk(VIDC_WARN, "This may result in undefined behavior\n");
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index c8946f98ced4..7727789dbda1 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -173,6 +173,7 @@ static void s5p_mfc_watchdog_worker(struct work_struct *work)
}
s5p_mfc_clock_on();
ret = s5p_mfc_init_hw(dev);
+ s5p_mfc_clock_off();
if (ret)
mfc_err("Failed to reinit FW\n");
}
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 2cdb740cde48..f838d9c7ed12 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1321,8 +1321,8 @@ static int mceusb_dev_probe(struct usb_interface *intf,
}
}
}
- if (ep_in == NULL) {
- dev_dbg(&intf->dev, "inbound and/or endpoint not found");
+ if (!ep_in || !ep_out) {
+ dev_dbg(&intf->dev, "required endpoints not found\n");
return -ENODEV;
}
diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c
index 317ef63ee789..8d96a22647b3 100644
--- a/drivers/media/tuners/tuner-xc2028.c
+++ b/drivers/media/tuners/tuner-xc2028.c
@@ -281,6 +281,14 @@ static void free_firmware(struct xc2028_data *priv)
int i;
tuner_dbg("%s called\n", __func__);
+ /* free allocated f/w string */
+ if (priv->fname != firmware_name)
+ kfree(priv->fname);
+ priv->fname = NULL;
+
+ priv->state = XC2028_NO_FIRMWARE;
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+
if (!priv->firm)
return;
@@ -291,9 +299,6 @@ static void free_firmware(struct xc2028_data *priv)
priv->firm = NULL;
priv->firm_size = 0;
- priv->state = XC2028_NO_FIRMWARE;
-
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
}
static int load_all_firmwares(struct dvb_frontend *fe,
@@ -884,9 +889,8 @@ read_not_reliable:
return 0;
fail:
- priv->state = XC2028_NO_FIRMWARE;
+ free_firmware(priv);
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
if (retry_count < 8) {
msleep(50);
retry_count++;
@@ -1332,11 +1336,8 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
mutex_lock(&xc2028_list_mutex);
/* only perform final cleanup if this is the last instance */
- if (hybrid_tuner_report_instance_count(priv) == 1) {
+ if (hybrid_tuner_report_instance_count(priv) == 1)
free_firmware(priv);
- kfree(priv->ctrl.fname);
- priv->ctrl.fname = NULL;
- }
if (priv)
hybrid_tuner_release_state(priv);
@@ -1399,19 +1400,8 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
/*
* Copy the config data.
- * For the firmware name, keep a local copy of the string,
- * in order to avoid troubles during device release.
*/
- kfree(priv->ctrl.fname);
- priv->ctrl.fname = NULL;
memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
- if (p->fname) {
- priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
- if (priv->ctrl.fname == NULL) {
- rc = -ENOMEM;
- goto unlock;
- }
- }
/*
* If firmware name changed, frees firmware. As free_firmware will
@@ -1426,10 +1416,15 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
if (priv->state == XC2028_NO_FIRMWARE) {
if (!firmware_name[0])
- priv->fname = priv->ctrl.fname;
+ priv->fname = kstrdup(p->fname, GFP_KERNEL);
else
priv->fname = firmware_name;
+ if (!priv->fname) {
+ rc = -ENOMEM;
+ goto unlock;
+ }
+
rc = request_firmware_nowait(THIS_MODULE, 1,
priv->fname,
priv->i2c_props.adap->dev.parent,
diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
index de4ae5eb4830..10d8a08e36e6 100644
--- a/drivers/media/usb/cx231xx/cx231xx-audio.c
+++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
@@ -671,10 +671,8 @@ static int cx231xx_audio_init(struct cx231xx *dev)
spin_lock_init(&adev->slock);
err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto err_free_card;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_cx231xx_pcm_capture);
@@ -688,10 +686,9 @@ static int cx231xx_audio_init(struct cx231xx *dev)
INIT_WORK(&dev->wq_trigger, audio_trigger);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto err_free_card;
+
adev->sndcard = card;
adev->udev = dev->udev;
@@ -701,6 +698,11 @@ static int cx231xx_audio_init(struct cx231xx *dev)
hs_config_info[0].interface_info.
audio_index + 1];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) {
+ err = -ENODEV;
+ goto err_free_card;
+ }
+
adev->end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress;
@@ -710,13 +712,20 @@ static int cx231xx_audio_init(struct cx231xx *dev)
"audio EndPoint Addr 0x%x, Alternate settings: %i\n",
adev->end_point_addr, adev->num_alt);
adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
-
- if (adev->alt_max_pkt_size == NULL)
- return -ENOMEM;
+ if (!adev->alt_max_pkt_size) {
+ err = -ENOMEM;
+ goto err_free_card;
+ }
for (i = 0; i < adev->num_alt; i++) {
- u16 tmp =
- le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) {
+ err = -ENODEV;
+ goto err_free_pkt_size;
+ }
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
wMaxPacketSize);
adev->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
@@ -726,6 +735,13 @@ static int cx231xx_audio_init(struct cx231xx *dev)
}
return 0;
+
+err_free_pkt_size:
+ kfree(adev->alt_max_pkt_size);
+err_free_card:
+ snd_card_free(card);
+
+ return err;
}
static int cx231xx_audio_fini(struct cx231xx *dev)
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 8389c162bc89..2c5f76d588ac 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -1447,6 +1447,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
uif = udev->actconfig->interface[idx];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
dev->video_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc.bEndpointAddress;
dev->video_mode.num_alt = uif->num_altsetting;
@@ -1460,7 +1463,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
return -ENOMEM;
for (i = 0; i < dev->video_mode.num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
dev->video_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
dev_dbg(dev->dev,
"Alternate setting %i, max size= %i\n", i,
@@ -1477,6 +1485,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
}
uif = udev->actconfig->interface[idx];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
dev->vbi_mode.end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress;
@@ -1493,8 +1504,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
return -ENOMEM;
for (i = 0; i < dev->vbi_mode.num_alt; i++) {
- u16 tmp =
- le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->vbi_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
@@ -1514,6 +1529,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
}
uif = udev->actconfig->interface[idx];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
dev->sliced_cc_mode.end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress;
@@ -1528,7 +1546,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
return -ENOMEM;
for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->sliced_cc_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
@@ -1693,6 +1716,11 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
}
uif = udev->actconfig->interface[idx];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) {
+ retval = -ENODEV;
+ goto err_video_alt;
+ }
+
dev->ts1_mode.end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].
desc.bEndpointAddress;
@@ -1710,7 +1738,14 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
}
for (i = 0; i < dev->ts1_mode.num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) {
+ retval = -ENODEV;
+ goto err_video_alt;
+ }
+
+ tmp = le16_to_cpu(uif->altsetting[i].
endpoint[isoc_pipe].desc.
wMaxPacketSize);
dev->ts1_mode.alt_max_pkt_size[i] =
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index ab58f0b9da5c..d1b4b729e814 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -783,6 +783,9 @@ int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf)
/* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
+ if (intf->altsetting[0].desc.bNumEndpoints < rc_ep + 1)
+ return -ENODEV;
+
purb = usb_alloc_urb(0, GFP_KERNEL);
if (purb == NULL) {
err("rc usb alloc urb failed");
diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c
index f10717311e05..dd93c2c8fea9 100644
--- a/drivers/media/usb/dvb-usb/ttusb2.c
+++ b/drivers/media/usb/dvb-usb/ttusb2.c
@@ -78,6 +78,9 @@ static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd,
u8 *s, *r = NULL;
int ret = 0;
+ if (4 + rlen > 64)
+ return -EIO;
+
s = kzalloc(wlen+4, GFP_KERNEL);
if (!s)
return -ENOMEM;
@@ -381,6 +384,22 @@ static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num
write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
read = msg[i].flags & I2C_M_RD;
+ if (3 + msg[i].len > sizeof(obuf)) {
+ err("i2c wr len=%d too high", msg[i].len);
+ break;
+ }
+ if (write_read) {
+ if (3 + msg[i+1].len > sizeof(ibuf)) {
+ err("i2c rd len=%d too high", msg[i+1].len);
+ break;
+ }
+ } else if (read) {
+ if (3 + msg[i].len > sizeof(ibuf)) {
+ err("i2c rd len=%d too high", msg[i].len);
+ break;
+ }
+ }
+
obuf[0] = (msg[i].addr << 1) | (write_read | read);
if (read)
obuf[1] = 0;
diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c
index 0712b1bc90b4..0f6d57fbf91b 100644
--- a/drivers/media/usb/gspca/konica.c
+++ b/drivers/media/usb/gspca/konica.c
@@ -188,6 +188,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
return -EIO;
}
+ if (alt->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index d1dc1a198e3e..91d709efef7a 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -1523,7 +1523,14 @@ static int usbvision_probe(struct usb_interface *intf,
}
for (i = 0; i < usbvision->num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < 2) {
+ ret = -ENODEV;
+ goto err_pkt;
+ }
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
wMaxPacketSize);
usbvision->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index 7433ba5c4bad..fd6a3b36208e 100644
--- a/drivers/media/usb/zr364xx/zr364xx.c
+++ b/drivers/media/usb/zr364xx/zr364xx.c
@@ -604,6 +604,14 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam,
ptr = pdest = frm->lpvbits;
if (frm->ulState == ZR364XX_READ_IDLE) {
+ if (purb->actual_length < 128) {
+ /* header incomplete */
+ dev_info(&cam->udev->dev,
+ "%s: buffer (%d bytes) too small to hold jpeg header. Discarding.\n",
+ __func__, purb->actual_length);
+ return -EINVAL;
+ }
+
frm->ulState = ZR364XX_READ_FRAME;
frm->cur_size = 0;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
index 5419bd1655c1..b292ea70fb40 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -26,11 +26,14 @@
#include <linux/debugfs.h>
#include <linux/msm_audio_ion.h>
#include <linux/compat.h>
+#include <linux/mutex.h>
#include "audio_utils_aio.h"
#ifdef CONFIG_USE_DEV_CTRL_VOLUME
#include <linux/qdsp6v2/audio_dev_ctl.h>
#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+static DEFINE_MUTEX(lock);
#ifdef CONFIG_DEBUG_FS
+
int audio_aio_debug_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -43,29 +46,37 @@ ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
const int debug_bufmax = 4096;
static char buffer[4096];
int n = 0;
- struct q6audio_aio *audio = file->private_data;
+ struct q6audio_aio *audio;
- mutex_lock(&audio->lock);
- n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "enabled %d\n", audio->enabled);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "stopped %d\n", audio->stopped);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "feedback %d\n", audio->feedback);
- mutex_unlock(&audio->lock);
- /* Following variables are only useful for debugging when
- * when playback halts unexpectedly. Thus, no mutual exclusion
- * enforced
- */
- n += scnprintf(buffer + n, debug_bufmax - n,
- "wflush %d\n", audio->wflush);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "rflush %d\n", audio->rflush);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "inqueue empty %d\n", list_empty(&audio->in_queue));
- n += scnprintf(buffer + n, debug_bufmax - n,
- "outqueue empty %d\n", list_empty(&audio->out_queue));
+ mutex_lock(&lock);
+ if (file->private_data != NULL) {
+ audio = file->private_data;
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n",
+ audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "feedback %d\n", audio->feedback);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "inqueue empty %d\n",
+ list_empty(&audio->in_queue));
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "outqueue empty %d\n",
+ list_empty(&audio->out_queue));
+ }
+ mutex_unlock(&lock);
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
@@ -573,6 +584,7 @@ int audio_aio_release(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = file->private_data;
pr_debug("%s[%pK]\n", __func__, audio);
+ mutex_lock(&lock);
mutex_lock(&audio->lock);
mutex_lock(&audio->read_lock);
mutex_lock(&audio->write_lock);
@@ -616,6 +628,8 @@ int audio_aio_release(struct inode *inode, struct file *file)
#endif
kfree(audio->codec_cfg);
kfree(audio);
+ file->private_data = NULL;
+ mutex_unlock(&lock);
return 0;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 7d09f22d3bc6..500185546599 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2908,7 +2908,11 @@ static int qseecom_send_service_cmd(struct qseecom_dev_handle *data,
}
if (req.cmd_id == QSEOS_RPMB_CHECK_PROV_STATUS_COMMAND) {
pr_warn("RPMB key status is 0x%x\n", resp.result);
- *(uint32_t *)req.resp_buf = resp.result;
+ if (put_user(resp.result,
+ (uint32_t __user *)req.resp_buf)) {
+ ret = -EINVAL;
+ goto exit;
+ }
ret = 0;
}
break;
@@ -6498,11 +6502,16 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
void *cmd_buf = NULL;
size_t cmd_len;
struct sglist_info *table = data->sglistinfo_ptr;
+ void *req_ptr = NULL;
+ void *resp_ptr = NULL;
ret = __qseecom_qteec_validate_msg(data, req);
if (ret)
return ret;
+ req_ptr = req->req_ptr;
+ resp_ptr = req->resp_ptr;
+
/* find app_id & img_name from list */
spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
@@ -6520,6 +6529,11 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
return -ENOENT;
}
+ req->req_ptr = (void *)__qseecom_uvirt_to_kvirt(data,
+ (uintptr_t)req->req_ptr);
+ req->resp_ptr = (void *)__qseecom_uvirt_to_kvirt(data,
+ (uintptr_t)req->resp_ptr);
+
if ((cmd_id == QSEOS_TEE_OPEN_SESSION) ||
(cmd_id == QSEOS_TEE_REQUEST_CANCELLATION)) {
ret = __qseecom_update_qteec_req_buf(
@@ -6531,10 +6545,10 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
if (qseecom.qsee_version < QSEE_VERSION_40) {
ireq.app_id = data->client.app_id;
ireq.req_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data,
- (uintptr_t)req->req_ptr);
+ (uintptr_t)req_ptr);
ireq.req_len = req->req_len;
ireq.resp_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data,
- (uintptr_t)req->resp_ptr);
+ (uintptr_t)resp_ptr);
ireq.resp_len = req->resp_len;
ireq.sglistinfo_ptr = (uint32_t)virt_to_phys(table);
ireq.sglistinfo_len = SGLISTINFO_TABLE_SIZE;
@@ -6545,10 +6559,10 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
} else {
ireq_64bit.app_id = data->client.app_id;
ireq_64bit.req_ptr = (uint64_t)__qseecom_uvirt_to_kphys(data,
- (uintptr_t)req->req_ptr);
+ (uintptr_t)req_ptr);
ireq_64bit.req_len = req->req_len;
ireq_64bit.resp_ptr = (uint64_t)__qseecom_uvirt_to_kphys(data,
- (uintptr_t)req->resp_ptr);
+ (uintptr_t)resp_ptr);
ireq_64bit.resp_len = req->resp_len;
if ((data->client.app_arch == ELFCLASS32) &&
((ireq_64bit.req_ptr >=
diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c
index ad21276c8d9e..091370f4ea40 100644
--- a/drivers/misc/uid_sys_stats.c
+++ b/drivers/misc/uid_sys_stats.c
@@ -49,7 +49,8 @@ struct io_stats {
#define UID_STATE_TOTAL_CURR 2
#define UID_STATE_TOTAL_LAST 3
-#define UID_STATE_SIZE 4
+#define UID_STATE_DEAD_TASKS 4
+#define UID_STATE_SIZE 5
struct uid_entry {
uid_t uid;
@@ -214,35 +215,44 @@ static u64 compute_write_bytes(struct task_struct *task)
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)
+static void add_uid_io_stats(struct uid_entry *uid_entry,
+ struct task_struct *task, int slot)
{
- struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
+ struct io_stats *io_slot = &uid_entry->io[slot];
- 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;
+ io_slot->read_bytes += task->ioac.read_bytes;
+ io_slot->write_bytes += compute_write_bytes(task);
+ io_slot->rchar += task->ioac.rchar;
+ io_slot->wchar += task->ioac.wchar;
+ io_slot->fsync += task->ioac.syscfs;
}
-static void clean_uid_io_last_stats(struct uid_entry *uid_entry,
- struct task_struct *task)
+static void compute_uid_io_bucket_stats(struct io_stats *io_bucket,
+ struct io_stats *io_curr,
+ struct io_stats *io_last,
+ struct io_stats *io_dead)
{
- struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
+ io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes -
+ io_last->read_bytes;
+ io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes -
+ io_last->write_bytes;
+ io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar;
+ io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar;
+ io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync;
- 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;
+ 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;
+
+ memset(io_dead, 0, sizeof(struct io_stats));
}
static void update_io_stats_all_locked(void)
{
struct uid_entry *uid_entry;
struct task_struct *task, *temp;
- struct io_stats *io_bucket, *io_curr, *io_last;
struct user_namespace *user_ns = current_user_ns();
unsigned long bkt;
uid_t uid;
@@ -257,70 +267,38 @@ static void update_io_stats_all_locked(void)
uid_entry = find_or_register_uid(uid);
if (!uid_entry)
continue;
- add_uid_io_curr_stats(uid_entry, task);
+ add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
} while_each_thread(temp, task);
rcu_read_unlock();
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;
+ compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+ &uid_entry->io[UID_STATE_TOTAL_CURR],
+ &uid_entry->io[UID_STATE_TOTAL_LAST],
+ &uid_entry->io[UID_STATE_DEAD_TASKS]);
}
}
-static void update_io_stats_uid_locked(uid_t target_uid)
+static void update_io_stats_uid_locked(struct uid_entry *uid_entry)
{
- struct uid_entry *uid_entry;
struct task_struct *task, *temp;
- struct io_stats *io_bucket, *io_curr, *io_last;
struct user_namespace *user_ns = current_user_ns();
- uid_entry = find_or_register_uid(target_uid);
- if (!uid_entry)
- return;
-
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
sizeof(struct io_stats));
rcu_read_lock();
do_each_thread(temp, task) {
- if (from_kuid_munged(user_ns, task_uid(task)) != target_uid)
+ if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid)
continue;
- add_uid_io_curr_stats(uid_entry, task);
+ add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
} while_each_thread(temp, task);
rcu_read_unlock();
- 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;
+ compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+ &uid_entry->io[UID_STATE_TOTAL_CURR],
+ &uid_entry->io[UID_STATE_TOTAL_LAST],
+ &uid_entry->io[UID_STATE_DEAD_TASKS]);
}
static int uid_io_show(struct seq_file *m, void *v)
@@ -405,7 +383,7 @@ static ssize_t uid_procstat_write(struct file *file,
return count;
}
- update_io_stats_uid_locked(uid);
+ update_io_stats_uid_locked(uid_entry);
uid_entry->state = state;
@@ -443,8 +421,7 @@ static int process_notifier(struct notifier_block *self,
uid_entry->utime += utime;
uid_entry->stime += stime;
- update_io_stats_uid_locked(uid);
- clean_uid_io_last_stats(uid_entry, task);
+ add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS);
exit:
rt_mutex_unlock(&uid_lock);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 63f7bf87843f..2cb0ea03a338 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -616,17 +616,39 @@ static int mmc_devfreq_create_freq_table(struct mmc_host *host)
host->card->clk_scaling_lowest,
host->card->clk_scaling_highest);
+ /*
+ * Create the frequency table and initialize it with default values.
+ * Initialize it with platform specific frequencies if the frequency
+ * table supplied by platform driver is present, otherwise initialize
+ * it with min and max frequencies supported by the card.
+ */
if (!clk_scaling->freq_table) {
- pr_debug("%s: no frequency table defined - setting default\n",
- mmc_hostname(host));
+ if (clk_scaling->pltfm_freq_table_sz)
+ clk_scaling->freq_table_sz =
+ clk_scaling->pltfm_freq_table_sz;
+ else
+ clk_scaling->freq_table_sz = 2;
+
clk_scaling->freq_table = kzalloc(
- 2*sizeof(*(clk_scaling->freq_table)), GFP_KERNEL);
+ (clk_scaling->freq_table_sz *
+ sizeof(*(clk_scaling->freq_table))), GFP_KERNEL);
if (!clk_scaling->freq_table)
return -ENOMEM;
- clk_scaling->freq_table[0] = host->card->clk_scaling_lowest;
- clk_scaling->freq_table[1] = host->card->clk_scaling_highest;
- clk_scaling->freq_table_sz = 2;
- goto out;
+
+ if (clk_scaling->pltfm_freq_table) {
+ memcpy(clk_scaling->freq_table,
+ clk_scaling->pltfm_freq_table,
+ (clk_scaling->pltfm_freq_table_sz *
+ sizeof(*(clk_scaling->pltfm_freq_table))));
+ } else {
+ pr_debug("%s: no frequency table defined - setting default\n",
+ mmc_hostname(host));
+ clk_scaling->freq_table[0] =
+ host->card->clk_scaling_lowest;
+ clk_scaling->freq_table[1] =
+ host->card->clk_scaling_highest;
+ goto out;
+ }
}
if (host->card->clk_scaling_lowest >
@@ -835,7 +857,7 @@ int mmc_resume_clk_scaling(struct mmc_host *host)
devfreq_min_clk = host->clk_scaling.freq_table[0];
host->clk_scaling.curr_freq = devfreq_max_clk;
- if (host->ios.clock < host->card->clk_scaling_highest)
+ if (host->ios.clock < host->clk_scaling.freq_table[max_clk_idx])
host->clk_scaling.curr_freq = devfreq_min_clk;
host->clk_scaling.clk_scaling_in_progress = false;
@@ -895,6 +917,10 @@ int mmc_exit_clk_scaling(struct mmc_host *host)
host->clk_scaling.devfreq = NULL;
atomic_set(&host->clk_scaling.devfreq_abort, 1);
+
+ kfree(host->clk_scaling.freq_table);
+ host->clk_scaling.freq_table = NULL;
+
pr_debug("%s: devfreq was removed\n", mmc_hostname(host));
return 0;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 9ca73a2b86db..ae54302be8fd 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -327,6 +327,7 @@ void mmc_retune_enable(struct mmc_host *host)
mod_timer(&host->retune_timer,
jiffies + host->retune_period * HZ);
}
+EXPORT_SYMBOL(mmc_retune_enable);
void mmc_retune_disable(struct mmc_host *host)
{
@@ -335,6 +336,7 @@ void mmc_retune_disable(struct mmc_host *host)
host->retune_now = 0;
host->need_retune = 0;
}
+EXPORT_SYMBOL(mmc_retune_disable);
void mmc_retune_timer_stop(struct mmc_host *host)
{
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 5b4d5d74fe55..5033107f6e26 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1283,6 +1283,11 @@ static int _mmc_sd_resume(struct mmc_host *host)
#else
err = mmc_sd_init_card(host, host->card->ocr, host->card);
#endif
+ if (err) {
+ pr_err("%s: %s: mmc_sd_init_card_failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto out;
+ }
mmc_card_clr_suspended(host->card);
if (host->card->sdr104_blocked)
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index ca72ebfd55a3..feb9cbe1f068 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1823,7 +1823,7 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
}
pdata->status_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
- if (gpio_is_valid(pdata->status_gpio) & !(flags & OF_GPIO_ACTIVE_LOW))
+ if (gpio_is_valid(pdata->status_gpio) && !(flags & OF_GPIO_ACTIVE_LOW))
pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
of_property_read_u32(np, "qcom,bus-width", &bus_width);
@@ -1837,13 +1837,13 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
}
if (sdhci_msm_dt_get_array(dev, "qcom,devfreq,freq-table",
- &msm_host->mmc->clk_scaling.freq_table,
- &msm_host->mmc->clk_scaling.freq_table_sz, 0))
+ &msm_host->mmc->clk_scaling.pltfm_freq_table,
+ &msm_host->mmc->clk_scaling.pltfm_freq_table_sz, 0))
pr_debug("%s: no clock scaling frequencies were supplied\n",
dev_name(dev));
- else if (!msm_host->mmc->clk_scaling.freq_table ||
- !msm_host->mmc->clk_scaling.freq_table_sz)
- dev_err(dev, "bad dts clock scaling frequencies\n");
+ else if (!msm_host->mmc->clk_scaling.pltfm_freq_table ||
+ !msm_host->mmc->clk_scaling.pltfm_freq_table_sz)
+ dev_err(dev, "bad dts clock scaling frequencies\n");
/*
* Few hosts can support DDR52 mode at the same lower
@@ -1958,7 +1958,7 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
sdhci_msm_pm_qos_parse(dev, pdata);
if (of_get_property(np, "qcom,core_3_0v_support", NULL))
- pdata->core_3_0v_support = true;
+ msm_host->core_3_0v_support = true;
pdata->sdr104_wa = of_property_read_bool(np, "qcom,sdr104-wa");
@@ -2639,7 +2639,9 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
*/
mb();
- if ((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & CORE_3_0V_SUPPORT))
+ if ((io_level & REQ_IO_HIGH) &&
+ (msm_host->caps_0 & CORE_3_0V_SUPPORT) &&
+ !msm_host->core_3_0v_support)
writel_relaxed((readl_relaxed(host->ioaddr +
msm_host_offset->CORE_VENDOR_SPEC) &
~CORE_IO_PAD_PWR_SWITCH), host->ioaddr +
@@ -4133,7 +4135,7 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
msm_host->use_14lpp_dll = true;
/* Fake 3.0V support for SDIO devices which requires such voltage */
- if (msm_host->pdata->core_3_0v_support) {
+ if (msm_host->core_3_0v_support) {
caps |= CORE_3_0V_SUPPORT;
writel_relaxed((readl_relaxed(host->ioaddr +
SDHCI_CAPABILITIES) | caps), host->ioaddr +
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 92f61708001e..b63b4df3ded3 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -152,7 +152,6 @@ struct sdhci_msm_pltfm_data {
u32 ice_clk_max;
u32 ice_clk_min;
struct sdhci_msm_pm_qos_data pm_qos_data;
- bool core_3_0v_support;
bool sdr104_wa;
};
@@ -226,6 +225,7 @@ struct sdhci_msm_host {
bool tuning_in_progress;
bool mci_removed;
const struct sdhci_msm_offset *offset;
+ bool core_3_0v_support;
};
extern char *saved_command_line;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 40a34c283955..ddb9947ce298 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2418,7 +2418,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->ops->platform_execute_tuning) {
spin_unlock_irqrestore(&host->lock, flags);
+ /*
+ * Make sure re-tuning won't get triggered for the CRC errors
+ * occurred while executing tuning
+ */
+ mmc_retune_disable(mmc);
err = host->ops->platform_execute_tuning(host, opcode);
+ mmc_retune_enable(mmc);
sdhci_runtime_pm_put(host);
return err;
}
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 25f21968fa5c..de2ea9f2f966 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -1077,7 +1077,7 @@ static int stir421x_patch_device(struct irda_usb_cb *self)
* are "42101001.sb" or "42101002.sb"
*/
sprintf(stir421x_fw_name, "4210%4X.sb",
- self->usbdev->descriptor.bcdDevice);
+ le16_to_cpu(self->usbdev->descriptor.bcdDevice));
ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev);
if (ret < 0)
return ret;
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 09052f9e324f..c6f5d9a6bec6 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -754,6 +754,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
{QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */
+ {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */
/* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 9acaffa51516..95412139e7f6 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -2375,6 +2375,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
init_completion(&ar->vdev_setup_done);
init_completion(&ar->thermal.wmi_sync);
init_completion(&ar->bss_survey_done);
+ init_completion(&ar->peer_delete_done);
INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 01d5ecc4f6b8..fa2e226ec085 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -957,7 +957,9 @@ struct ath10k {
struct fw_flag *fw_flags;
/* set for bmi chip sets */
+ struct completion peer_delete_done;
bool is_bmi;
+ enum ieee80211_sta_state sta_state;
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index b9d08b4b4cc5..313eb43cbf2c 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -782,6 +782,7 @@ static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
{
int ret;
+ unsigned long time_left;
lockdep_assert_held(&ar->conf_mutex);
@@ -793,6 +794,16 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
if (ret)
return ret;
+ if (QCA_REV_WCN3990(ar)) {
+ time_left = wait_for_completion_timeout(&ar->peer_delete_done,
+ 5 * HZ);
+
+ if (time_left == 0) {
+ ath10k_warn(ar, "Timeout in receiving peer delete response\n");
+ return -ETIMEDOUT;
+ }
+ }
+
ar->num_peers--;
return 0;
@@ -5924,6 +5935,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
new_state == IEEE80211_STA_NOTEXIST))
cancel_work_sync(&arsta->update_wk);
+ if (vif->type == NL80211_IFTYPE_STATION && new_state > ar->sta_state)
+ ar->sta_state = new_state;
+
mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -7392,8 +7406,9 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
ctx, arvif->vdev_id);
WARN_ON(!arvif->is_started);
-
- if (vif->type == NL80211_IFTYPE_MONITOR) {
+ if (vif->type == NL80211_IFTYPE_MONITOR ||
+ (vif->type == NL80211_IFTYPE_STATION &&
+ ar->sta_state < IEEE80211_STA_ASSOC)) {
WARN_ON(!arvif->is_up);
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
@@ -7409,6 +7424,7 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
ath10k_warn(ar, "failed to stop vdev %i: %d\n",
arvif->vdev_id, ret);
+ ar->sta_state = IEEE80211_STA_NOTEXIST;
arvif->is_started = false;
mutex_unlock(&ar->conf_mutex);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 75f2528b8b84..36026a15f721 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -412,6 +412,15 @@ static int ath10k_wmi_tlv_event_tx_pause(struct ath10k *ar,
return 0;
}
+static int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TLV_PEER_DELETE_RESP_EVENTID\n");
+ complete(&ar->peer_delete_done);
+
+ return 0;
+}
+
/***********/
/* TLV ops */
/***********/
@@ -552,6 +561,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_TLV_TX_PAUSE_EVENTID:
ath10k_wmi_tlv_event_tx_pause(ar, skb);
break;
+ case WMI_TLV_PEER_DELETE_RESP_EVENTID:
+ ath10k_wmi_tlv_event_peer_delete_resp(ar, skb);
+ break;
default:
ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id);
break;
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
index 79f324f132e9..f8139bcf79cc 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -313,6 +313,8 @@ enum wmi_tlv_event_id {
WMI_TLV_PEER_TX_FAIL_CNT_THR_EVENTID,
WMI_TLV_PEER_ESTIMATED_LINKSPEED_EVENTID,
WMI_TLV_PEER_STATE_EVENTID,
+ WMI_TLV_PEER_ASSOC_CONF_EVENTID,
+ WMI_TLV_PEER_DELETE_RESP_EVENTID,
WMI_TLV_MGMT_RX_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MGMT),
WMI_TLV_HOST_SWBA_EVENTID,
WMI_TLV_TBTTOFFSET_UPDATE_EVENTID,
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 165dd202c365..c92564b3ec85 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -37,6 +37,7 @@ static struct usb_device_id ath9k_hif_usb_ids[] = {
{ USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */
{ USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */
{ USB_DEVICE(0x0471, 0x209e) }, /* Philips (or NXP) PTA01 */
+ { USB_DEVICE(0x1eda, 0x2315) }, /* AirTies */
{ USB_DEVICE(0x0cf3, 0x7015),
.driver_info = AR9287_USB }, /* Atheros */
@@ -1216,6 +1217,9 @@ static int send_eject_command(struct usb_interface *interface)
u8 bulk_out_ep;
int r;
+ if (iface_desc->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
/* Find bulk out endpoint */
for (r = 1; r >= 0; r--) {
endpoint = &iface_desc->endpoint[r].desc;
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c
index 95a7fdb3cc1c..c602a1e674ca 100644
--- a/drivers/net/wireless/cw1200/sta.c
+++ b/drivers/net/wireless/cw1200/sta.c
@@ -2135,9 +2135,7 @@ void cw1200_mcast_timeout(unsigned long arg)
int cw1200_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn,
- u8 buf_size, bool amsdu)
+ struct ieee80211_ampdu_params *params)
{
/* Aggregation is implemented fully in firmware,
* including block ack negotiation. Do not allow
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index 6656215a13a9..04b0349a6ad9 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -5982,12 +5982,14 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
int
il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid, u16 * ssn,
- u8 buf_size, bool amsdu)
+ struct ieee80211_ampdu_params *params)
{
struct il_priv *il = hw->priv;
int ret = -EINVAL;
+ struct ieee80211_sta *sta = params->sta;
+ enum ieee80211_ampdu_mlme_action action = params->action;
+ u16 tid = params->tid;
+ u16 *ssn = &params->ssn;
D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid);
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index b3ad34e8bf5a..1eb1a823a111 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -729,12 +729,15 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn,
- u8 buf_size, bool amsdu)
+ struct ieee80211_ampdu_params *params)
{
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
int ret = -EINVAL;
+ struct ieee80211_sta *sta = params->sta;
+ enum ieee80211_ampdu_mlme_action action = params->action;
+ u16 tid = params->tid;
+ u16 *ssn = &params->ssn;
+ u8 buf_size = params->buf_size;
struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n",
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index ce12717e656a..1a8ea775de08 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -826,13 +826,16 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid,
- u16 *ssn, u8 buf_size, bool amsdu)
+ struct ieee80211_ampdu_params *params)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
bool tx_agg_ref = false;
+ struct ieee80211_sta *sta = params->sta;
+ enum ieee80211_ampdu_mlme_action action = params->action;
+ u16 tid = params->tid;
+ u16 *ssn = &params->ssn;
+ u8 buf_size = params->buf_size;
IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
sta->addr, tid, action);
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c
index 21192b6f9c64..268e50ba88a5 100644
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ b/drivers/net/wireless/mwifiex/pcie.c
@@ -947,6 +947,7 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
if (card && card->cmd_buf) {
mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(card->cmd_buf);
}
return 0;
}
@@ -1513,6 +1514,11 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
return -1;
card->cmd_buf = skb;
+ /*
+ * Need to keep a reference, since core driver might free up this
+ * buffer before we've unmapped it.
+ */
+ skb_get(skb);
/* To send a command, the driver will:
1. Write the 64bit physical address of the data buffer to
@@ -1610,6 +1616,7 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
if (card->cmd_buf) {
mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(card->cmd_buf);
card->cmd_buf = NULL;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
index 9b4d8a637915..4b354918e183 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
@@ -359,6 +359,107 @@ bool rtl8821ae_phy_rf_config(struct ieee80211_hw *hw)
return rtl8821ae_phy_rf6052_config(hw);
}
+static void _rtl8812ae_phy_set_rfe_reg_24g(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 tmp;
+
+ switch (rtlhal->rfe_type) {
+ case 3:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337770);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337770);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1);
+ break;
+ case 4:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x001);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x001);
+ break;
+ case 5:
+ rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x77);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3);
+ rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp & ~0x1);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ break;
+ case 1:
+ if (rtlpriv->btcoexist.bt_coexistence) {
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x777777);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
+ 0x77777777);
+ rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ break;
+ }
+ case 0:
+ case 2:
+ default:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ break;
+ }
+}
+
+static void _rtl8812ae_phy_set_rfe_reg_5g(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 tmp;
+
+ switch (rtlhal->rfe_type) {
+ case 0:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337717);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337717);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ break;
+ case 1:
+ if (rtlpriv->btcoexist.bt_coexistence) {
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x337717);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
+ 0x77337717);
+ rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ } else {
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD,
+ 0x77337717);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
+ 0x77337717);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ }
+ break;
+ case 3:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337717);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337717);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1);
+ break;
+ case 5:
+ rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x33);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777);
+ tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3);
+ rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp | 0x1);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ break;
+ case 2:
+ case 4:
+ default:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337777);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ break;
+ }
+}
+
u32 phy_get_tx_swing_8812A(struct ieee80211_hw *hw, u8 band,
u8 rf_path)
{
@@ -553,14 +654,9 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band)
/* 0x82C[1:0] = 2b'00 */
rtl_set_bbreg(hw, 0x82c, 0x3, 0);
}
- if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
- rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD,
- 0x77777777);
- rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
- 0x77777777);
- rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x000);
- rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x000);
- }
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
+ _rtl8812ae_phy_set_rfe_reg_24g(hw);
rtl_set_bbreg(hw, RTXPATH, 0xf0, 0x1);
rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0x1);
@@ -615,14 +711,8 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band)
/* 0x82C[1:0] = 2'b00 */
rtl_set_bbreg(hw, 0x82c, 0x3, 1);
- if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
- rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD,
- 0x77337777);
- rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
- 0x77337777);
- rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x010);
- rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x010);
- }
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
+ _rtl8812ae_phy_set_rfe_reg_5g(hw);
rtl_set_bbreg(hw, RTXPATH, 0xf0, 0);
rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0xf);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h
index 1d6110f9c1fb..ed69dbe178ff 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h
@@ -2424,6 +2424,7 @@
#define BMASKH4BITS 0xf0000000
#define BMASKOFDM_D 0xffc00000
#define BMASKCCK 0x3f3f3f3f
+#define BMASKRFEINV 0x3ff00000
#define BRFREGOFFSETMASK 0xfffff
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
index 09c7e098f460..085ef5c87262 100644
--- a/drivers/net/wireless/ti/wl18xx/event.c
+++ b/drivers/net/wireless/ti/wl18xx/event.c
@@ -206,5 +206,33 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl)
mbox->sc_pwd_len,
mbox->sc_pwd);
+ if (vector & RX_BA_WIN_SIZE_CHANGE_EVENT_ID) {
+ struct wl12xx_vif *wlvif;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ u8 link_id = mbox->rx_ba_link_id;
+ u8 win_size = mbox->rx_ba_win_size;
+ const u8 *addr;
+
+ wlvif = wl->links[link_id].wlvif;
+ vif = wl12xx_wlvif_to_vif(wlvif);
+
+ /* Update RX aggregation window size and call
+ * MAC routine to stop active RX aggregations for this link
+ */
+ if (wlvif->bss_type != BSS_TYPE_AP_BSS)
+ addr = vif->bss_conf.bssid;
+ else
+ addr = wl->links[link_id].addr;
+
+ sta = ieee80211_find_sta(vif, addr);
+ if (sta) {
+ sta->max_rx_aggregation_subframes = win_size;
+ ieee80211_stop_rx_ba_session(vif,
+ wl->links[link_id].ba_bitmap,
+ addr);
+ }
+ }
+
return 0;
}
diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h
index f3d4f13379cb..9495fadc8093 100644
--- a/drivers/net/wireless/ti/wl18xx/event.h
+++ b/drivers/net/wireless/ti/wl18xx/event.h
@@ -38,6 +38,7 @@ enum {
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18),
DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19),
PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20),
+ RX_BA_WIN_SIZE_CHANGE_EVENT_ID = BIT(21),
SMART_CONFIG_SYNC_EVENT_ID = BIT(22),
SMART_CONFIG_DECODE_EVENT_ID = BIT(23),
TIME_SYNC_EVENT_ID = BIT(24),
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 50cce42089a5..47f355e92193 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -1029,7 +1029,8 @@ static int wl18xx_boot(struct wl1271 *wl)
DFS_CHANNELS_CONFIG_COMPLETE_EVENT |
SMART_CONFIG_SYNC_EVENT_ID |
SMART_CONFIG_DECODE_EVENT_ID |
- TIME_SYNC_EVENT_ID;
+ TIME_SYNC_EVENT_ID |
+ RX_BA_WIN_SIZE_CHANGE_EVENT_ID;
wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index f28fa3b5029d..0646c9b6f8d7 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -1419,7 +1419,8 @@ out:
/* setup BA session receiver setting in the FW. */
int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
- u16 ssn, bool enable, u8 peer_hlid)
+ u16 ssn, bool enable, u8 peer_hlid,
+ u8 win_size)
{
struct wl1271_acx_ba_receiver_setup *acx;
int ret;
@@ -1435,7 +1436,7 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
acx->hlid = peer_hlid;
acx->tid = tid_index;
acx->enable = enable;
- acx->win_size = wl->conf.ht.rx_ba_win_size;
+ acx->win_size = win_size;
acx->ssn = ssn;
ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx,
diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h
index 954d57ec98f4..524aea495dff 100644
--- a/drivers/net/wireless/ti/wlcore/acx.h
+++ b/drivers/net/wireless/ti/wlcore/acx.h
@@ -1112,7 +1112,8 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl,
int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
- u16 ssn, bool enable, u8 peer_hlid);
+ u16 ssn, bool enable, u8 peer_hlid,
+ u8 win_size);
int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u64 *mactime);
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 30165ea0fa25..7b27c7e23af2 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5328,7 +5328,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
}
ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
- hlid);
+ hlid,
+ params->buf_size);
+
if (!ret) {
*ba_bitmap |= BIT(tid);
wl->ba_rx_session_count++;
@@ -5349,7 +5351,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
}
ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false,
- hlid);
+ hlid, 0);
if (!ret) {
*ba_bitmap &= ~BIT(tid);
wl->ba_rx_session_count--;
diff --git a/drivers/of/address.c b/drivers/of/address.c
index ec5eb17ae283..5393be762da4 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -260,7 +260,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
if (!parser->range || parser->range + parser->np > parser->end)
return NULL;
- range->pci_space = parser->range[0];
+ range->pci_space = be32_to_cpup(parser->range);
range->flags = of_bus_pci_get_flags(parser->range);
range->pci_addr = of_read_number(parser->range + 1, ns);
range->cpu_addr = of_translate_address(parser->node,
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 1aebd49220b0..217c7ce3f57b 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -4675,6 +4675,8 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options)
goto link_fail;
}
+ msleep(500);
+
msm_pcie_config_controller(dev);
if (!dev->msi_gicm_addr)
@@ -5608,16 +5610,18 @@ static void msm_pcie_unmap_qgic_addr(struct msm_pcie_dev_t *dev,
}
}
-void msm_pcie_destroy_irq(unsigned int irq)
+void msm_pcie_destroy_irq(unsigned int irq, struct pci_dev *pdev)
{
int pos;
- struct pci_dev *pdev = irq_get_chip_data(irq);
struct msi_desc *entry = irq_get_msi_desc(irq);
struct msi_desc *firstentry;
struct msm_pcie_dev_t *dev;
u32 nvec;
int firstirq;
+ if (!pdev)
+ pdev = irq_get_chip_data(irq);
+
if (!pdev) {
pr_err("PCIe: pci device is null. IRQ:%d\n", irq);
return;
@@ -5677,7 +5681,7 @@ void msm_pcie_destroy_irq(unsigned int irq)
void arch_teardown_msi_irq(unsigned int irq)
{
PCIE_GEN_DBG("irq %d deallocated\n", irq);
- msm_pcie_destroy_irq(irq);
+ msm_pcie_destroy_irq(irq, NULL);
}
void arch_teardown_msi_irqs(struct pci_dev *dev)
@@ -5696,7 +5700,7 @@ void arch_teardown_msi_irqs(struct pci_dev *dev)
continue;
nvec = 1 << entry->msi_attrib.multiple;
for (i = 0; i < nvec; i++)
- arch_teardown_msi_irq(entry->irq + i);
+ msm_pcie_destroy_irq(entry->irq + i, dev);
}
}
@@ -5892,7 +5896,6 @@ static int arch_setup_msi_irq_qgic(struct pci_dev *pdev,
firstirq = irq;
irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
- irq_set_chip_data(irq, pdev);
}
/* write msi vector and data */
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index d7508704c992..f8b2b5987ea9 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -973,15 +973,19 @@ void pci_remove_legacy_files(struct pci_bus *b)
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
enum pci_mmap_api mmap_api)
{
- unsigned long nr, start, size, pci_start;
+ unsigned long nr, start, size;
+ resource_size_t pci_start = 0, pci_end;
if (pci_resource_len(pdev, resno) == 0)
return 0;
nr = vma_pages(vma);
start = vma->vm_pgoff;
size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
- pci_start = (mmap_api == PCI_MMAP_PROCFS) ?
- pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0;
+ if (mmap_api == PCI_MMAP_PROCFS) {
+ pci_resource_to_user(pdev, resno, &pdev->resource[resno],
+ &pci_start, &pci_end);
+ pci_start >>= PAGE_SHIFT;
+ }
if (start >= pci_start && start < pci_start + size &&
start + nr <= pci_start + size)
return 1;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 0e53488f8ec1..1a14ca8965e6 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1732,8 +1732,8 @@ static void pci_pme_list_scan(struct work_struct *work)
}
}
if (!list_empty(&pci_pme_list))
- schedule_delayed_work(&pci_pme_work,
- msecs_to_jiffies(PME_TIMEOUT));
+ queue_delayed_work(system_freezable_wq, &pci_pme_work,
+ msecs_to_jiffies(PME_TIMEOUT));
mutex_unlock(&pci_pme_list_mutex);
}
@@ -1798,8 +1798,9 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
mutex_lock(&pci_pme_list_mutex);
list_add(&pme_dev->list, &pci_pme_list);
if (list_is_singular(&pci_pme_list))
- schedule_delayed_work(&pci_pme_work,
- msecs_to_jiffies(PME_TIMEOUT));
+ queue_delayed_work(system_freezable_wq,
+ &pci_pme_work,
+ msecs_to_jiffies(PME_TIMEOUT));
mutex_unlock(&pci_pme_list_mutex);
} else {
mutex_lock(&pci_pme_list_mutex);
diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.h b/drivers/phy/phy-qcom-ufs-qmp-v3.h
index 0c9c3e7896bf..8cb4b0eeb866 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-v3.h
+++ b/drivers/phy/phy-qcom-ufs-qmp-v3.h
@@ -259,7 +259,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_0_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A),
@@ -320,7 +320,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_LANE_MODE_1, 0x06),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_LVL, 0x24),
@@ -336,7 +336,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E),
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index 40c1971dfe96..10a49b1e75d8 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -600,12 +600,12 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx,
mem_size = (ipa_ctx->hdr_proc_ctx_tbl_lcl) ?
IPA_MEM_PART(apps_hdr_proc_ctx_size) :
IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr);
- if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) {
- IPAERR("hdr proc ctx table overflow\n");
- goto bad_len;
- }
-
if (list_empty(&htbl->head_free_offset_list[bin])) {
+ if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) {
+ IPAERR("hdr proc ctx table overflow\n");
+ goto bad_len;
+ }
+
offset = kmem_cache_zalloc(ipa_ctx->hdr_proc_ctx_offset_cache,
GFP_KERNEL);
if (!offset) {
@@ -711,30 +711,30 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
mem_size = (ipa_ctx->hdr_tbl_lcl) ? IPA_MEM_PART(apps_hdr_size) :
IPA_MEM_PART(apps_hdr_size_ddr);
- /*
- * if header does not fit to table, place it in DDR
- * This is valid for IPA 2.5 and on,
- * with the exception of IPA2.6L.
- */
- if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) {
- if (ipa_ctx->ipa_hw_type != IPA_HW_v2_5) {
- IPAERR("not enough room for header\n");
- goto bad_hdr_len;
- } else {
- entry->is_hdr_proc_ctx = true;
- entry->phys_base = dma_map_single(ipa_ctx->pdev,
- 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;
+ if (list_empty(&htbl->head_free_offset_list[bin])) {
+ /*
+ * if header does not fit to table, place it in DDR
+ * This is valid for IPA 2.5 and on,
+ * with the exception of IPA2.6L.
+ */
+ if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) {
+ if (ipa_ctx->ipa_hw_type != IPA_HW_v2_5) {
+ IPAERR("not enough room for header\n");
+ goto bad_hdr_len;
+ } else {
+ entry->is_hdr_proc_ctx = true;
+ entry->phys_base = dma_map_single(ipa_ctx->pdev,
+ entry->hdr,
+ entry->hdr_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ipa_ctx->pdev,
+ entry->phys_base)) {
+ IPAERR("dma_map_single failureed\n");
+ goto fail_dma_mapping;
+ }
}
- }
- } else {
- entry->is_hdr_proc_ctx = false;
- if (list_empty(&htbl->head_free_offset_list[bin])) {
+ } else {
+ entry->is_hdr_proc_ctx = false;
offset = kmem_cache_zalloc(ipa_ctx->hdr_offset_cache,
GFP_KERNEL);
if (!offset) {
@@ -751,14 +751,15 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
htbl->end += ipa_hdr_bin_sz[bin];
list_add(&offset->link,
&htbl->head_offset_list[bin]);
- } else {
- /* get the first free slot */
- offset =
- list_first_entry(&htbl->head_free_offset_list[bin],
- struct ipa_hdr_offset_entry, link);
- list_move(&offset->link, &htbl->head_offset_list[bin]);
+ entry->offset_entry = offset;
}
-
+ } else {
+ entry->is_hdr_proc_ctx = false;
+ /* get the first free slot */
+ offset =
+ list_first_entry(&htbl->head_free_offset_list[bin],
+ struct ipa_hdr_offset_entry, link);
+ list_move(&offset->link, &htbl->head_offset_list[bin]);
entry->offset_entry = offset;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c
index 364cd4b7d38a..69c88bd04b1b 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c
@@ -665,6 +665,12 @@ send_cmd:
retries++;
if (retries == IPA_BAM_STOP_MAX_RETRY) {
IPAERR("Failed after %d tries\n", retries);
+ mutex_unlock(&ipa_ctx->uc_ctx.uc_lock);
+ /*
+ * Max retry reached,
+ * assert to check why cmd send failed.
+ */
+ ipa_assert();
} else {
/* sleep for short period to flush IPA */
usleep_range(IPA_UC_WAIT_MIN_SLEEP,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 9943095abe30..e0200fe50871 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -1008,6 +1008,11 @@ enum ipacm_client_enum ipa2_get_client(int pipe_idx)
*/
bool ipa2_get_client_uplink(int pipe_idx)
{
+ if (pipe_idx < 0 || pipe_idx >= IPA_MAX_NUM_PIPES) {
+ IPAERR("invalid pipe idx %d\n", pipe_idx);
+ return false;
+ }
+
return ipa_ctx->ipacm_client[pipe_idx].uplink;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index e197ee8b06dd..a5186f1aff35 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -377,12 +377,12 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx,
mem_size = (ipa3_ctx->hdr_proc_ctx_tbl_lcl) ?
IPA_MEM_PART(apps_hdr_proc_ctx_size) :
IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr);
- if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) {
- IPAERR("hdr proc ctx table overflow\n");
- goto bad_len;
- }
-
if (list_empty(&htbl->head_free_offset_list[bin])) {
+ if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) {
+ IPAERR("hdr proc ctx table overflow\n");
+ goto bad_len;
+ }
+
offset = kmem_cache_zalloc(ipa3_ctx->hdr_proc_ctx_offset_cache,
GFP_KERNEL);
if (!offset) {
@@ -487,20 +487,21 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
mem_size = (ipa3_ctx->hdr_tbl_lcl) ? IPA_MEM_PART(apps_hdr_size) :
IPA_MEM_PART(apps_hdr_size_ddr);
- /* if header does not fit to table, place it in DDR */
- if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) {
- entry->is_hdr_proc_ctx = true;
- entry->phys_base = dma_map_single(ipa3_ctx->pdev,
- entry->hdr,
- entry->hdr_len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(ipa3_ctx->pdev, entry->phys_base)) {
- IPAERR("dma_map_single failure for entry\n");
- goto fail_dma_mapping;
- }
- } else {
- entry->is_hdr_proc_ctx = false;
- if (list_empty(&htbl->head_free_offset_list[bin])) {
+ if (list_empty(&htbl->head_free_offset_list[bin])) {
+ /* if header does not fit to table, place it in DDR */
+ if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) {
+ entry->is_hdr_proc_ctx = true;
+ entry->phys_base = dma_map_single(ipa3_ctx->pdev,
+ entry->hdr,
+ entry->hdr_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ipa3_ctx->pdev,
+ entry->phys_base)) {
+ IPAERR("dma_map_single failure for entry\n");
+ goto fail_dma_mapping;
+ }
+ } else {
+ entry->is_hdr_proc_ctx = false;
offset = kmem_cache_zalloc(ipa3_ctx->hdr_offset_cache,
GFP_KERNEL);
if (!offset) {
@@ -517,14 +518,14 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
htbl->end += ipa_hdr_bin_sz[bin];
list_add(&offset->link,
&htbl->head_offset_list[bin]);
- } else {
- /* get the first free slot */
- offset =
- list_first_entry(&htbl->head_free_offset_list[bin],
- struct ipa_hdr_offset_entry, link);
- list_move(&offset->link, &htbl->head_offset_list[bin]);
+ entry->offset_entry = offset;
}
-
+ } else {
+ entry->is_hdr_proc_ctx = false;
+ /* get the first free slot */
+ offset = list_first_entry(&htbl->head_free_offset_list[bin],
+ struct ipa_hdr_offset_entry, link);
+ list_move(&offset->link, &htbl->head_offset_list[bin]);
entry->offset_entry = offset;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index d19de2a7bdb5..6647f919a577 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1064,6 +1064,11 @@ enum ipacm_client_enum ipa3_get_client(int pipe_idx)
*/
bool ipa3_get_client_uplink(int pipe_idx)
{
+ if (pipe_idx < 0 || pipe_idx >= IPA3_MAX_NUM_PIPES) {
+ IPAERR("invalid pipe idx %d\n", pipe_idx);
+ return false;
+ }
+
return ipa3_ctx->ipacm_client[pipe_idx].uplink;
}
diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c
index 4354b2600472..68ef2595f3c3 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.c
+++ b/drivers/platform/msm/mhi/mhi_bhi.c
@@ -537,11 +537,12 @@ void bhi_firmware_download(struct work_struct *work)
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");
- wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ ret = wait_event_interruptible_timeout(
+ *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_BHI ||
- mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT);
- if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT ||
- mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) {
+ mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
+ if (!ret || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"MHI is not in valid state for firmware download\n");
return;
diff --git a/drivers/platform/msm/mhi/mhi_macros.h b/drivers/platform/msm/mhi/mhi_macros.h
index 04ecf13991b3..ee0b9b759e0a 100644
--- a/drivers/platform/msm/mhi/mhi_macros.h
+++ b/drivers/platform/msm/mhi/mhi_macros.h
@@ -27,8 +27,7 @@
#define CMD_EL_PER_RING 128
#define ELEMENT_GAP 1
#define MHI_EPID 4
-#define MHI_MAX_RESUME_TIMEOUT 5000
-#define MHI_MAX_SUSPEND_TIMEOUT 5000
+#define MHI_MAX_STATE_TRANSITION_TIMEOUT 5000
#define MHI_MAX_CMD_TIMEOUT 500
#define MHI_RPM_AUTOSUSPEND_TMR_VAL_MS 1000
#define MAX_BUF_SIZE 32
diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c
index ad9a6fd6b278..49db99100311 100644
--- a/drivers/platform/msm/mhi/mhi_pm.c
+++ b/drivers/platform/msm/mhi/mhi_pm.c
@@ -116,7 +116,7 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
mhi_dev_ctxt->mhi_state == MHI_STATE_M1 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M0||M1 event or LD pm_state:0x%x state:%s\n",
@@ -142,7 +142,7 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_M3 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M3 event, timeout, current state:%s\n",
@@ -180,7 +180,7 @@ static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
mhi_dev_ctxt->mhi_state == MHI_STATE_M1 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M0 event, timeout or LD\n");
@@ -322,9 +322,6 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
else
ret_val = 0;
- /* wait for firmware download to complete */
- flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work);
-
if (ret_val) {
read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
@@ -333,6 +330,9 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
unlock_pm_lock:
+ /* wait for firmware download to complete */
+ flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work);
+
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", ret_val);
mutex_unlock(&mhi_dev_ctxt->pm_lock);
return ret_val;
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index c5346babf310..18736547d422 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -467,6 +467,7 @@ extern void dump_sram(u8 *buf, int addr, int len);
extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos);
extern s64 fg_float_decode(u16 val);
extern bool is_input_present(struct fg_chip *chip);
+extern bool is_qnovo_en(struct fg_chip *chip);
extern void fg_circ_buf_add(struct fg_circ_buf *, int);
extern void fg_circ_buf_clr(struct fg_circ_buf *);
extern int fg_circ_buf_avg(struct fg_circ_buf *, int *);
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index f2395b6ba4ab..9635044e02a5 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -106,14 +106,17 @@ static struct fg_dbgfs dbgfs_data = {
static bool is_usb_present(struct fg_chip *chip)
{
union power_supply_propval pval = {0, };
+ int rc;
if (!chip->usb_psy)
chip->usb_psy = power_supply_get_by_name("usb");
- if (chip->usb_psy)
- power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- else
+ if (!chip->usb_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0)
return false;
return pval.intval != 0;
@@ -122,14 +125,17 @@ static bool is_usb_present(struct fg_chip *chip)
static bool is_dc_present(struct fg_chip *chip)
{
union power_supply_propval pval = {0, };
+ int rc;
if (!chip->dc_psy)
chip->dc_psy = power_supply_get_by_name("dc");
- if (chip->dc_psy)
- power_supply_get_property(chip->dc_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- else
+ if (!chip->dc_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0)
return false;
return pval.intval != 0;
@@ -140,6 +146,25 @@ bool is_input_present(struct fg_chip *chip)
return is_usb_present(chip) || is_dc_present(chip);
}
+bool is_qnovo_en(struct fg_chip *chip)
+{
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (!chip->batt_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &pval);
+ if (rc < 0)
+ return false;
+
+ return pval.intval != 0;
+}
+
#define EXPONENT_SHIFT 11
#define EXPONENT_OFFSET -9
#define MANTISSA_SIGN_BIT 10
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index bd7231705319..81cd4b89ddbf 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -1402,6 +1402,7 @@ out:
static void fg_cap_learning_update(struct fg_chip *chip)
{
int rc, batt_soc, batt_soc_msb;
+ bool input_present = is_input_present(chip);
mutex_lock(&chip->cl.lock);
@@ -1442,11 +1443,29 @@ static void fg_cap_learning_update(struct fg_chip *chip)
chip->cl.init_cc_uah = 0;
}
+ if (chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (!input_present) {
+ fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
+ batt_soc_msb);
+ chip->cl.active = false;
+ chip->cl.init_cc_uah = 0;
+ }
+ }
+
if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
- fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
- batt_soc_msb);
- chip->cl.active = false;
- chip->cl.init_cc_uah = 0;
+ if (is_qnovo_en(chip) && input_present) {
+ /*
+ * Don't abort the capacity learning when qnovo
+ * is enabled and input is present where the
+ * charging status can go to "not charging"
+ * intermittently.
+ */
+ } else {
+ fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
+ batt_soc_msb);
+ chip->cl.active = false;
+ chip->cl.init_cc_uah = 0;
+ }
}
}
@@ -1981,7 +2000,7 @@ static int fg_esr_fcc_config(struct fg_chip *chip)
{
union power_supply_propval prop = {0, };
int rc;
- bool parallel_en = false, qnovo_en = false;
+ bool parallel_en = false, qnovo_en;
if (is_parallel_charger_available(chip)) {
rc = power_supply_get_property(chip->parallel_psy,
@@ -1994,10 +2013,7 @@ static int fg_esr_fcc_config(struct fg_chip *chip)
parallel_en = prop.intval;
}
- rc = power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &prop);
- if (!rc)
- qnovo_en = prop.intval;
+ qnovo_en = is_qnovo_en(chip);
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,
@@ -4479,7 +4495,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
/* Keep BSOC_DELTA_IRQ irq disabled until we require it */
- rerun_election(chip->delta_bsoc_irq_en_votable);
+ vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);
rc = fg_debugfs_create(chip);
if (rc < 0) {
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 88956d3ba674..068c7ddfb739 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -344,6 +344,15 @@ config REGULATOR_MAX1586
regulator via I2C bus. The provided regulator is suitable
for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
+config REGULATOR_MAX20010
+ tristate "Maxim MAX20010 regulator support"
+ depends on I2C
+ help
+ This driver supports the Maxim MAX20010 switching voltage regulator
+ (buck converter). The regulator is controlled using an I2C interface
+ and supports 2 programmable voltage ranges from 0.5V to 1.27V in 10mV
+ steps and 0.625V to 1.5875V in 12.5mV steps.
+
config REGULATOR_MAX8649
tristate "Maxim 8649 voltage regulator"
depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e345f10f94af..856ebe2b5c1b 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
obj-$(CONFIG_REGULATOR_MAX14577) += max14577.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
+obj-$(CONFIG_REGULATOR_MAX20010) += max20010-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
diff --git a/drivers/regulator/max20010-regulator.c b/drivers/regulator/max20010-regulator.c
new file mode 100644
index 000000000000..a914ca70ccb7
--- /dev/null
+++ b/drivers/regulator/max20010-regulator.c
@@ -0,0 +1,490 @@
+/* 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/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+struct voltage_range {
+ int vrange_sel;
+ int min_uV;
+ int max_uV;
+ int step_uV;
+};
+
+struct max20010_slew_rate {
+ int slew_sel;
+ int soft_start;
+ int dvs;
+};
+
+struct max20010_device_info {
+ struct device *dev;
+ struct regulator_dev *rdev;
+ struct regulator_init_data *init_data;
+ struct regmap *regmap;
+ const struct voltage_range *range;
+ const struct max20010_slew_rate *slew_rate;
+ unsigned vout_sel;
+ bool enabled;
+};
+
+#define MAX20010_ID_REG 0x00
+
+#define MAX20010_VMAX_REG 0x02
+#define MAX20010_VMAX_MASK GENMASK(6, 0)
+
+#define MAX20010_CONFIG_REG 0x05
+#define MAX20010_CONFIG_SYNC_IO_MASK GENMASK(1, 0)
+#define MAX20010_CONFIG_MODE_MASK BIT(3)
+#define MAX20010_CONFIG_MODE_SYNC 0
+#define MAX20010_CONFIG_MODE_FPWM 8
+#define MAX20010_CONFIG_VSTEP_MASK BIT(7)
+#define MAX20010_CONFIG_VSTEP_SHIFT 7
+
+#define MAX20010_SLEW_REG 0x06
+#define MAX20010_SLEW_MASK GENMASK(3, 0)
+
+#define MAX20010_VSET_REG 0x07
+#define MAX20010_VSET_MASK GENMASK(6, 0)
+
+static const struct max20010_slew_rate slew_rates[] = {
+ {0, 22000, 22000},
+ {1, 11000, 22000},
+ {2, 5500, 22000},
+ {3, 11000, 11000},
+ {4, 5500, 11000},
+ {5, 44000, 44000},
+ {6, 22000, 44000},
+ {7, 11000, 44000},
+ {8, 5500, 44000},
+ {9, 5500, 5500},
+};
+
+static const struct voltage_range max20010_range0 = {0, 500000, 1270000, 10000};
+static const struct voltage_range max20010_range1 = {1, 625000, 1587500, 12500};
+
+static const struct regmap_config max20010_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX20010_VSET_REG,
+};
+
+static int max20010_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ /* Set the voltage only if the regulator was enabled earlier */
+ if (info->enabled) {
+ rc = regulator_set_voltage_sel_regmap(rdev, sel);
+ if (rc) {
+ dev_err(info->dev,
+ "regulator set voltage failed for selector = 0x%2x, rc=%d\n",
+ sel, rc);
+ return rc;
+ }
+ }
+
+ info->vout_sel = sel;
+ return rc;
+}
+
+static int max20010_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+
+ return (info->enabled == true) ? 1 : 0;
+}
+
+static int max20010_regulator_enable(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ rc = regulator_set_voltage_sel_regmap(rdev, info->vout_sel);
+ if (rc) {
+ dev_err(info->dev, "regulator enable failed, rc=%d\n", rc);
+ return rc;
+ }
+ info->enabled = true;
+
+ return rc;
+}
+
+static int max20010_regulator_disable(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ rc = regulator_set_voltage_sel_regmap(rdev, 0x0);
+ if (rc) {
+ dev_err(info->dev, "regulator disable failed, rc=%d\n", rc);
+ return rc;
+ }
+ info->enabled = false;
+
+ return rc;
+}
+
+static inline unsigned int max20010_map_mode(unsigned int mode)
+{
+ return (mode == MAX20010_CONFIG_MODE_FPWM) ?
+ REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
+}
+
+static int max20010_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ MAX20010_CONFIG_MODE_MASK,
+ MAX20010_CONFIG_MODE_FPWM);
+ break;
+ case REGULATOR_MODE_IDLE:
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ (MAX20010_CONFIG_MODE_MASK
+ | MAX20010_CONFIG_SYNC_IO_MASK),
+ MAX20010_CONFIG_MODE_SYNC);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rc)
+ dev_err(info->dev, "failed to set %s mode, rc=%d\n",
+ mode == REGULATOR_MODE_NORMAL ? "Force PWM" : "SYNC",
+ rc);
+ return rc;
+}
+
+static unsigned int max20010_get_mode(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int rc = 0;
+
+ rc = regmap_read(info->regmap, MAX20010_CONFIG_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read mode configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return max20010_map_mode(val & MAX20010_CONFIG_MODE_MASK);
+}
+
+static int max20010_enable_time(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int volt_uV;
+
+ volt_uV = regulator_list_voltage_linear(rdev, info->vout_sel);
+ return DIV_ROUND_UP(volt_uV, info->slew_rate->soft_start);
+}
+
+static struct regulator_ops max20010_regulator_ops = {
+ .set_voltage_sel = max20010_set_voltage_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .map_voltage = regulator_map_voltage_linear,
+ .list_voltage = regulator_list_voltage_linear,
+ .is_enabled = max20010_regulator_is_enabled,
+ .enable = max20010_regulator_enable,
+ .disable = max20010_regulator_disable,
+ .set_mode = max20010_set_mode,
+ .get_mode = max20010_get_mode,
+ .enable_time = max20010_enable_time,
+};
+
+static struct regulator_desc rdesc = {
+ .name = "max20010-reg",
+ .supply_name = "vin",
+ .owner = THIS_MODULE,
+ .ops = &max20010_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_min_sel = 1,
+ .vsel_reg = MAX20010_VSET_REG,
+ .vsel_mask = MAX20010_VSET_MASK,
+ .of_map_mode = max20010_map_mode,
+};
+
+static int max20010_device_setup(struct max20010_device_info *info)
+{
+ int max_uV, rc = 0;
+ unsigned int val;
+
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ MAX20010_CONFIG_VSTEP_MASK,
+ (info->range->vrange_sel
+ << MAX20010_CONFIG_VSTEP_SHIFT));
+ if (rc) {
+ dev_err(info->dev, "failed to update vstep configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ max_uV = min(info->init_data->constraints.max_uV, info->range->max_uV);
+ val = DIV_ROUND_UP(max_uV - info->range->min_uV,
+ info->range->step_uV) + 1;
+ rc = regmap_update_bits(info->regmap, MAX20010_VMAX_REG,
+ MAX20010_VMAX_MASK, val);
+ if (rc) {
+ dev_err(info->dev, "failed to write VMAX configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = regmap_update_bits(info->regmap, MAX20010_SLEW_REG,
+ MAX20010_SLEW_MASK, info->slew_rate->slew_sel);
+ if (rc) {
+ dev_err(info->dev, "failed to write slew configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Store default voltage register value */
+ rc = regmap_read(info->regmap, MAX20010_VSET_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read voltage register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ info->vout_sel = val & MAX20010_VSET_MASK;
+ info->enabled = (info->vout_sel != 0x0) ? true : false;
+
+ return rc;
+}
+
+static int max20010_parse_init_data(struct max20010_device_info *info)
+{
+ struct device_node *of_node = info->dev->of_node;
+ int i, slew_index, ss_slew_rate, dvs_slew_rate, rc = 0;
+ unsigned int val;
+
+ if (of_find_property(of_node, "maxim,vrange-sel", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,vrange-sel", &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,vrange-sel property read failed, rc=%d\n",
+ rc);
+ return rc;
+ } else if (val > 1) {
+ dev_err(info->dev, "unsupported vrange-sel value = %d, should be either 0 or 1\n",
+ val);
+ return -EINVAL;
+ }
+ } else {
+ /* Read default voltage range value */
+ rc = regmap_read(info->regmap, MAX20010_CONFIG_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read config register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = (val & MAX20010_CONFIG_VSTEP_MASK)
+ >> MAX20010_CONFIG_VSTEP_SHIFT;
+ }
+
+ info->range = (val == 0) ? &max20010_range0 : &max20010_range1;
+
+ /*
+ * Verify the min and max constraints specified through regulator device
+ * properties are fit with in that of the selected voltage range of the
+ * device.
+ */
+ if (info->init_data->constraints.min_uV < info->range->min_uV ||
+ info->init_data->constraints.max_uV > info->range->max_uV) {
+ dev_err(info->dev,
+ "Regulator min/max constraints are not fit with in the device min/max constraints\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Read soft-start and dvs slew rates from device node. Use default
+ * values if not specified.
+ *
+ * Read the register default values and modify them with the slew-rates
+ * defined through device node.
+ */
+ rc = regmap_read(info->regmap, MAX20010_SLEW_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read slew register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ slew_index = val & MAX20010_SLEW_MASK;
+
+ if (slew_index >= ARRAY_SIZE(slew_rates)) {
+ dev_err(info->dev, "unsupported default slew configuration\n");
+ return -EINVAL;
+ }
+
+ ss_slew_rate = slew_rates[slew_index].soft_start;
+ dvs_slew_rate = slew_rates[slew_index].dvs;
+
+ if (of_find_property(of_node, "maxim,soft-start-slew-rate", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,soft-start-slew-rate",
+ &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,soft-start-slew-rate read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ ss_slew_rate = val;
+ }
+
+ if (of_find_property(of_node, "maxim,dvs-slew-rate", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,dvs-slew-rate",
+ &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,dvs-slew-rate read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dvs_slew_rate = val;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(slew_rates); i++) {
+ if (ss_slew_rate == slew_rates[i].soft_start
+ && dvs_slew_rate == slew_rates[i].dvs) {
+ info->slew_rate = &slew_rates[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(slew_rates)) {
+ dev_err(info->dev, "invalid slew-rate values are specified.\n");
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int max20010_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max20010_device_info *info;
+ struct regulator_config config = { };
+ int val, rc = 0;
+
+ info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &client->dev;
+ info->init_data = of_get_regulator_init_data(info->dev,
+ info->dev->of_node, &rdesc);
+ if (!info->init_data) {
+ dev_err(info->dev, "regulator init_data is missing\n");
+ return -ENODEV;
+ }
+
+ info->init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;
+ info->init_data->constraints.valid_modes_mask
+ = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+
+ info->regmap = devm_regmap_init_i2c(client, &max20010_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ dev_err(info->dev, "Error in allocating regmap\n");
+ return PTR_ERR(info->regmap);
+ }
+
+ i2c_set_clientdata(client, info);
+
+ /* Get chip Id */
+ rc = regmap_read(info->regmap, MAX20010_ID_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "Failed to get chip ID!\n");
+ return rc;
+ }
+
+ rc = max20010_parse_init_data(info);
+ if (rc) {
+ dev_err(info->dev, "max20010 init data parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = max20010_device_setup(info);
+ if (rc) {
+ dev_err(info->dev, "Failed to setup device, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ config.dev = info->dev;
+ config.init_data = info->init_data;
+ config.regmap = info->regmap;
+ config.driver_data = info;
+ config.of_node = client->dev.of_node;
+
+ rdesc.min_uV = info->range->min_uV;
+ rdesc.uV_step = info->range->step_uV;
+ rdesc.n_voltages = DIV_ROUND_UP((info->range->max_uV
+ - info->range->min_uV),
+ info->range->step_uV);
+ rdesc.ramp_delay = info->slew_rate->dvs;
+
+ info->rdev = devm_regulator_register(info->dev, &rdesc, &config);
+ if (IS_ERR(info->rdev)) {
+ dev_err(info->dev, "Failed to register regulator, rc=%d\n", rc);
+ return PTR_ERR(info->rdev);
+ }
+
+ dev_info(info->dev, "Detected regulator MAX20010 PID = %d : voltage-range(%d) : (%d - %d) uV, step = %d uV\n",
+ val, info->range->vrange_sel, info->range->min_uV,
+ info->range->max_uV, info->range->step_uV);
+
+ return rc;
+}
+
+static const struct of_device_id max20010_match_table[] = {
+ {.compatible = "maxim,max20010", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max20010_match_table);
+
+static const struct i2c_device_id max20010_id[] = {
+ {"max20010", -1},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, max20010_id);
+
+static struct i2c_driver max20010_regulator_driver = {
+ .driver = {
+ .name = "max20010-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = max20010_match_table,
+ },
+ .probe = max20010_regulator_probe,
+ .id_table = max20010_id,
+};
+module_i2c_driver(max20010_regulator_driver);
+
+MODULE_DESCRIPTION("MAX20010 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c
index be59947ba6f2..d469463a0b00 100644
--- a/drivers/regulator/spm-regulator.c
+++ b/drivers/regulator/spm-regulator.c
@@ -95,6 +95,7 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000,
#define QPNP_SMPS_REG_VOLTAGE_SETPOINT 0x41
#define QPNP_SMPS_REG_MODE 0x45
#define QPNP_SMPS_REG_STEP_CTRL 0x61
+#define QPNP_SMPS_REG_UL_LL_CTRL 0x68
/* FTS426 voltage control registers */
#define QPNP_FTS426_REG_VOLTAGE_LB 0x40
@@ -102,6 +103,22 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000,
#define QPNP_FTS426_REG_VOLTAGE_VALID_LB 0x42
#define QPNP_FTS426_REG_VOLTAGE_VALID_UB 0x43
+/* HF voltage limit registers */
+#define QPNP_HF_REG_VOLTAGE_ULS 0x69
+#define QPNP_HF_REG_VOLTAGE_LLS 0x6B
+
+/* FTS voltage limit registers */
+#define QPNP_FTS_REG_VOLTAGE_ULS_VALID 0x6A
+#define QPNP_FTS_REG_VOLTAGE_LLS_VALID 0x6C
+
+/* FTS426 voltage limit registers */
+#define QPNP_FTS426_REG_VOLTAGE_ULS_LB 0x68
+#define QPNP_FTS426_REG_VOLTAGE_ULS_UB 0x69
+
+/* Common regulator UL & LL limits control register layout */
+#define QPNP_COMMON_UL_EN_MASK 0x80
+#define QPNP_COMMON_LL_EN_MASK 0x40
+
#define QPNP_SMPS_MODE_PWM 0x80
#define QPNP_SMPS_MODE_AUTO 0x40
#define QPNP_FTS426_MODE_PWM 0x07
@@ -924,6 +941,88 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg)
return rc;
}
+static int qpnp_smps_check_constraints(struct spm_vreg *vreg,
+ struct regulator_init_data *init_data)
+{
+ int rc = 0, limit_min_uV, limit_max_uV;
+ u16 ul_reg, ll_reg;
+ u8 reg[2];
+
+ limit_min_uV = 0;
+ limit_max_uV = INT_MAX;
+
+ ul_reg = QPNP_FTS_REG_VOLTAGE_ULS_VALID;
+ ll_reg = QPNP_FTS_REG_VOLTAGE_LLS_VALID;
+
+ switch (vreg->regulator_type) {
+ case QPNP_TYPE_HF:
+ ul_reg = QPNP_HF_REG_VOLTAGE_ULS;
+ ll_reg = QPNP_HF_REG_VOLTAGE_LLS;
+ case QPNP_TYPE_FTS2:
+ case QPNP_TYPE_FTS2p5:
+ rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr
+ + QPNP_SMPS_REG_UL_LL_CTRL, reg, 1);
+ if (rc) {
+ dev_err(&vreg->pdev->dev, "%s: UL_LL register read failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (reg[0] & QPNP_COMMON_UL_EN_MASK) {
+ rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr
+ + ul_reg, &reg[1], 1);
+ if (rc) {
+ dev_err(&vreg->pdev->dev, "%s: ULS register read failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ limit_max_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]);
+ }
+
+ if (reg[0] & QPNP_COMMON_LL_EN_MASK) {
+ rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr
+ + ll_reg, &reg[1], 1);
+ if (rc) {
+ dev_err(&vreg->pdev->dev, "%s: LLS register read failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ limit_min_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]);
+ }
+
+ break;
+ case QPNP_TYPE_FTS426:
+ rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr
+ + QPNP_FTS426_REG_VOLTAGE_ULS_LB,
+ reg, 2);
+ if (rc) {
+ dev_err(&vreg->pdev->dev, "%s: could not read voltage limit registers, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ limit_max_uV = spm_regulator_vlevel_to_uv(vreg,
+ ((unsigned)reg[1] << 8) | reg[0]);
+ break;
+ case QPNP_TYPE_ULT_HF:
+ /* no HW voltage limit configuration */
+ break;
+ }
+
+ if (init_data->constraints.min_uV < limit_min_uV
+ || init_data->constraints.max_uV > limit_max_uV) {
+ dev_err(&vreg->pdev->dev, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n",
+ init_data->constraints.min_uV,
+ init_data->constraints.max_uV, limit_min_uV,
+ limit_max_uV);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
static bool spm_regulator_using_range0(struct spm_vreg *vreg)
{
return vreg->range == &fts2_range0 || vreg->range == &fts2p5_range0
@@ -1105,6 +1204,13 @@ static int spm_regulator_probe(struct platform_device *pdev)
return -EINVAL;
}
+ rc = qpnp_smps_check_constraints(vreg, init_data);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: regulator constraints check failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
vreg->rdesc.name = init_data->constraints.name;
vreg->rdesc.type = REGULATOR_VOLTAGE;
vreg->rdesc.owner = THIS_MODULE;
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index d2c3d7cc35f5..5ca6d2130593 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -311,8 +311,7 @@ static int tps_65023_probe(struct i2c_client *client,
/* Enable setting output voltage by I2C */
regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2,
- TPS65023_REG_CTRL2_CORE_ADJ,
- TPS65023_REG_CTRL2_CORE_ADJ);
+ TPS65023_REG_CTRL2_CORE_ADJ, 0);
return 0;
}
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index aadaef7d1bed..aae796678ffe 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -2093,9 +2093,6 @@ static int ufs_qcom_init(struct ufs_hba *hba)
struct ufs_qcom_host *host;
struct resource *res;
- if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev)))
- return -ENODEV;
-
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host) {
err = -ENOMEM;
@@ -2787,6 +2784,24 @@ static int ufs_qcom_probe(struct platform_device *pdev)
{
int err;
struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ /*
+ * On qcom platforms, bootdevice is the primary storage
+ * device. This device can either be eMMC or UFS.
+ * The type of device connected is detected at runtime.
+ * So, if an eMMC device is connected, and this function
+ * is invoked, it would turn-off the regulator if it detects
+ * that the storage device is not ufs.
+ * These regulators are turned ON by the bootloaders & turning
+ * them off without sending PON may damage the connected device.
+ * Hence, check for the connected device early-on & don't turn-off
+ * the regulators.
+ */
+ if (of_property_read_bool(np, "non-removable") &&
+ strlen(android_boot_dev) &&
+ strcmp(android_boot_dev, dev_name(dev)))
+ return -ENODEV;
/* Perform generic probe */
err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_variant);
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a90c51a113d2..544a71e7c242 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -365,6 +365,8 @@ static int ufshcd_disable_clocks(struct ufs_hba *hba,
bool is_gating_context);
static int ufshcd_disable_clocks_skip_ref_clk(struct ufs_hba *hba,
bool is_gating_context);
+static void ufshcd_hold_all(struct ufs_hba *hba);
+static void ufshcd_release_all(struct ufs_hba *hba);
static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
static inline void ufshcd_save_tstamp_of_last_dme_cmd(struct ufs_hba *hba);
@@ -516,7 +518,7 @@ static inline void ufshcd_remove_non_printable(char *val)
*val = ' ';
}
-#define UFSHCD_MAX_CMD_LOGGING 100
+#define UFSHCD_MAX_CMD_LOGGING 200
#ifdef CONFIG_TRACEPOINTS
static inline void ufshcd_add_command_trace(struct ufs_hba *hba,
@@ -592,7 +594,7 @@ static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
ufshcd_cmd_log(hba, str, "dme", 0xff, cmd_id, 0xff);
}
-static void ufshcd_cmd_log_print(struct ufs_hba *hba)
+static void ufshcd_print_cmd_log(struct ufs_hba *hba)
{
int i;
int pos;
@@ -641,7 +643,7 @@ static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
{
}
-static void ufshcd_cmd_log_print(struct ufs_hba *hba)
+static void ufshcd_print_cmd_log(struct ufs_hba *hba)
{
}
#endif
@@ -2038,6 +2040,22 @@ out:
return;
}
+static void __ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba,
+ unsigned long delay_ms)
+{
+ pm_runtime_get_sync(hba->dev);
+ ufshcd_hold_all(hba);
+ ufshcd_scsi_block_requests(hba);
+ down_write(&hba->lock);
+ /* wait for all the outstanding requests to finish */
+ ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
+ ufshcd_set_auto_hibern8_timer(hba, delay_ms);
+ up_write(&hba->lock);
+ ufshcd_scsi_unblock_requests(hba);
+ ufshcd_release_all(hba);
+ pm_runtime_put_sync(hba->dev);
+}
+
static void ufshcd_hibern8_exit_work(struct work_struct *work)
{
int ret;
@@ -2089,19 +2107,32 @@ static ssize_t ufshcd_hibern8_on_idle_delay_store(struct device *dev,
{
struct ufs_hba *hba = dev_get_drvdata(dev);
unsigned long flags, value;
+ bool change = true;
if (kstrtoul(buf, 0, &value))
return -EINVAL;
spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->hibern8_on_idle.delay_ms == value)
+ change = false;
+
+ if (value >= hba->clk_gating.delay_ms_pwr_save ||
+ value >= hba->clk_gating.delay_ms_perf) {
+ dev_err(hba->dev, "hibern8_on_idle_delay (%lu) can not be >= to clkgate_delay_ms_pwr_save (%lu) and clkgate_delay_ms_perf (%lu)\n",
+ value, hba->clk_gating.delay_ms_pwr_save,
+ hba->clk_gating.delay_ms_perf);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return -EINVAL;
+ }
+
hba->hibern8_on_idle.delay_ms = value;
spin_unlock_irqrestore(hba->host->host_lock, flags);
/* Update auto hibern8 timer value if supported */
- if (ufshcd_is_auto_hibern8_supported(hba) &&
+ if (change && ufshcd_is_auto_hibern8_supported(hba) &&
hba->hibern8_on_idle.is_enabled)
- ufshcd_set_auto_hibern8_timer(hba,
- hba->hibern8_on_idle.delay_ms);
+ __ufshcd_set_auto_hibern8_timer(hba,
+ hba->hibern8_on_idle.delay_ms);
return count;
}
@@ -2131,7 +2162,7 @@ static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev,
/* Update auto hibern8 timer value if supported */
if (ufshcd_is_auto_hibern8_supported(hba)) {
- ufshcd_set_auto_hibern8_timer(hba,
+ __ufshcd_set_auto_hibern8_timer(hba,
value ? hba->hibern8_on_idle.delay_ms : value);
goto update;
}
@@ -3285,8 +3316,10 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba,
/*
* May get invoked from shutdown and IOCTL contexts.
* In shutdown context, it comes in with lock acquired.
+ * In error recovery context, it may come with lock acquired.
*/
- if (!ufshcd_is_shutdown_ongoing(hba))
+
+ if (!ufshcd_is_shutdown_ongoing(hba) && !ufshcd_eh_in_progress(hba))
down_read(&hba->lock);
/*
@@ -3320,7 +3353,7 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba,
out_put_tag:
ufshcd_put_dev_cmd_tag(hba, tag);
wake_up(&hba->dev_cmd.tag_wq);
- if (!ufshcd_is_shutdown_ongoing(hba))
+ if (!ufshcd_is_shutdown_ongoing(hba) && !ufshcd_eh_in_progress(hba))
up_read(&hba->lock);
return err;
}
@@ -4268,6 +4301,7 @@ out:
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_host_regs(hba);
+ ufshcd_print_cmd_log(hba);
}
ufshcd_save_tstamp_of_last_dme_cmd(hba);
@@ -6096,7 +6130,7 @@ static void ufshcd_err_handler(struct work_struct *work)
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_tmrs(hba, hba->outstanding_tasks);
- ufshcd_cmd_log_print(hba);
+ ufshcd_print_cmd_log(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
}
}
@@ -6608,7 +6642,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
hba = shost_priv(host);
tag = cmd->request->tag;
- ufshcd_cmd_log_print(hba);
+ ufshcd_print_cmd_log(hba);
lrbp = &hba->lrb[tag];
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp);
if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 561b9074f2ee..29e67d9fa389 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -4143,6 +4143,7 @@ static void glink_core_link_down(struct glink_transport_if *if_ptr)
rwref_write_get(&xprt_ptr->xprt_state_lhb0);
xprt_ptr->next_lcid = 1;
xprt_ptr->local_state = GLINK_XPRT_DOWN;
+ xprt_ptr->curr_qos_rate_kBps = 0;
xprt_ptr->local_version_idx = xprt_ptr->versions_entries - 1;
xprt_ptr->remote_version_idx = xprt_ptr->versions_entries - 1;
xprt_ptr->l_features =
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 168db46084df..8385987e8888 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -849,7 +849,7 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx)
rcu_id = srcu_read_lock(&einfo->use_ref);
- if (unlikely(!einfo->rx_fifo)) {
+ if (unlikely(!einfo->rx_fifo) && atomic_ctx) {
if (!get_rx_fifo(einfo)) {
srcu_read_unlock(&einfo->use_ref, rcu_id);
return;
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 43d954a0f7c7..28f89bfac7c6 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -168,6 +168,76 @@ enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_MAX,
};
+enum icnss_msa_perm {
+ ICNSS_MSA_PERM_HLOS_ALL = 0,
+ ICNSS_MSA_PERM_WLAN_HW_RW = 1,
+ ICNSS_MSA_PERM_DUMP_COLLECT = 2,
+ ICNSS_MSA_PERM_MAX,
+};
+
+#define ICNSS_MAX_VMIDS 4
+
+struct icnss_mem_region_info {
+ uint64_t reg_addr;
+ uint32_t size;
+ uint8_t secure_flag;
+ enum icnss_msa_perm perm;
+};
+
+struct icnss_msa_perm_list_t {
+ int vmids[ICNSS_MAX_VMIDS];
+ int perms[ICNSS_MAX_VMIDS];
+ int nelems;
+};
+
+struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = {
+ [ICNSS_MSA_PERM_HLOS_ALL] = {
+ .vmids = {VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
+ .nelems = 1,
+ },
+
+ [ICNSS_MSA_PERM_WLAN_HW_RW] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE},
+ .nelems = 2,
+ },
+
+ [ICNSS_MSA_PERM_DUMP_COLLECT] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ},
+ .nelems = 3,
+ },
+};
+
+struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
+ [ICNSS_MSA_PERM_HLOS_ALL] = {
+ .vmids = {VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
+ .nelems = 1,
+ },
+
+ [ICNSS_MSA_PERM_WLAN_HW_RW] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE},
+ .nelems = 3,
+ },
+
+ [ICNSS_MSA_PERM_DUMP_COLLECT] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE, VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ},
+ .nelems = 4,
+ },
+};
+
struct icnss_event_pd_service_down_data {
bool crashed;
bool fw_rejuvenate;
@@ -191,8 +261,8 @@ enum icnss_driver_state {
ICNSS_FW_TEST_MODE,
ICNSS_PM_SUSPEND,
ICNSS_PM_SUSPEND_NOIRQ,
- ICNSS_SSR_ENABLED,
- ICNSS_PDR_ENABLED,
+ ICNSS_SSR_REGISTERED,
+ ICNSS_PDR_REGISTERED,
ICNSS_PD_RESTART,
ICNSS_MSA0_ASSIGNED,
ICNSS_WLFW_EXISTS,
@@ -366,8 +436,6 @@ static struct icnss_priv {
bool bypass_s1_smmu;
} *penv;
-static enum cnss_cc_src cnss_cc_source = CNSS_SOURCE_CORE;
-
#ifdef CONFIG_ICNSS_DEBUG
static void icnss_ignore_qmi_timeout(bool ignore)
{
@@ -377,6 +445,84 @@ static void icnss_ignore_qmi_timeout(bool ignore)
static void icnss_ignore_qmi_timeout(bool ignore) { }
#endif
+static int icnss_assign_msa_perm(struct icnss_mem_region_info
+ *mem_region, enum icnss_msa_perm new_perm)
+{
+ int ret = 0;
+ phys_addr_t addr;
+ u32 size;
+ u32 i = 0;
+ u32 source_vmids[ICNSS_MAX_VMIDS];
+ u32 source_nelems;
+ u32 dest_vmids[ICNSS_MAX_VMIDS];
+ u32 dest_perms[ICNSS_MAX_VMIDS];
+ u32 dest_nelems;
+ enum icnss_msa_perm cur_perm = mem_region->perm;
+ struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
+
+ addr = mem_region->reg_addr;
+ size = mem_region->size;
+
+ if (mem_region->secure_flag) {
+ new_perm_list = &msa_perm_secure_list[new_perm];
+ old_perm_list = &msa_perm_secure_list[cur_perm];
+ } else {
+ new_perm_list = &msa_perm_list[new_perm];
+ old_perm_list = &msa_perm_list[cur_perm];
+ }
+
+ source_nelems = old_perm_list->nelems;
+ dest_nelems = new_perm_list->nelems;
+
+ for (i = 0; i < source_nelems; ++i)
+ source_vmids[i] = old_perm_list->vmids[i];
+
+ for (i = 0; i < dest_nelems; ++i) {
+ dest_vmids[i] = new_perm_list->vmids[i];
+ dest_perms[i] = new_perm_list->perms[i];
+ }
+
+ ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
+ dest_vmids, dest_perms, dest_nelems);
+ if (ret) {
+ icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
+ &addr, size, ret);
+ goto out;
+ }
+
+ icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
+ "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
+ source_nelems, source_vmids[0], source_vmids[1],
+ source_vmids[2], source_vmids[3], dest_nelems,
+ dest_vmids[0], dest_vmids[1], dest_vmids[2],
+ dest_vmids[3]);
+out:
+ return ret;
+}
+
+static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
+ enum icnss_msa_perm new_perm)
+{
+ int ret;
+ int i;
+ enum icnss_msa_perm old_perm;
+
+ for (i = 0; i < priv->nr_mem_region; i++) {
+ old_perm = priv->mem_region[i].perm;
+ ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
+ if (ret)
+ goto err_unmap;
+ priv->mem_region[i].perm = new_perm;
+ }
+ return 0;
+
+err_unmap:
+ for (i--; i >= 0; i--) {
+ icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
+ }
+ return ret;
+}
+
static void icnss_pm_stay_awake(struct icnss_priv *priv)
{
if (atomic_inc_return(&priv->pm_count) != 1)
@@ -941,18 +1087,6 @@ static int icnss_hw_power_off(struct icnss_priv *priv)
return ret;
}
-void cnss_set_cc_source(enum cnss_cc_src cc_source)
-{
- cnss_cc_source = cc_source;
-}
-EXPORT_SYMBOL(cnss_set_cc_source);
-
-enum cnss_cc_src cnss_get_cc_source(void)
-{
- return cnss_cc_source;
-}
-EXPORT_SYMBOL(cnss_get_cc_source);
-
int icnss_power_on(struct device *dev)
{
struct icnss_priv *priv = dev_get_drvdata(dev);
@@ -994,119 +1128,6 @@ int icnss_power_off(struct device *dev)
}
EXPORT_SYMBOL(icnss_power_off);
-static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region)
-{
- int ret = 0;
- phys_addr_t addr;
- u32 size;
- u32 source_vmlist[1] = {VMID_HLOS};
- int dest_vmids[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
- int dest_perms[3] = {PERM_READ|PERM_WRITE,
- PERM_READ|PERM_WRITE,
- PERM_READ|PERM_WRITE};
- int source_nelems = sizeof(source_vmlist)/sizeof(u32);
- int dest_nelems = 0;
-
- addr = mem_region->reg_addr;
- size = mem_region->size;
-
- if (!mem_region->secure_flag) {
- dest_vmids[2] = VMID_WLAN_CE;
- dest_nelems = 3;
- } else {
- dest_vmids[2] = 0;
- dest_nelems = 2;
- }
- ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
- dest_vmids, dest_perms, dest_nelems);
- if (ret) {
- icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
- &addr, size, ret);
- goto out;
- }
-
- icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
- source_vmlist[0], dest_nelems, dest_vmids[0],
- dest_vmids[1], dest_vmids[2]);
-out:
- return ret;
-
-}
-
-static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region)
-{
- int ret = 0;
- phys_addr_t addr;
- u32 size;
- u32 dest_vmids[1] = {VMID_HLOS};
- int source_vmlist[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
- int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
- int source_nelems = 0;
- int dest_nelems = sizeof(dest_vmids)/sizeof(u32);
-
- addr = mem_region->reg_addr;
- size = mem_region->size;
-
- if (!mem_region->secure_flag) {
- source_vmlist[2] = VMID_WLAN_CE;
- source_nelems = 3;
- } else {
- source_vmlist[2] = 0;
- source_nelems = 2;
- }
-
- ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
- dest_vmids, dest_perms, dest_nelems);
- if (ret) {
- icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n",
- &addr, size, ret);
- goto out;
- }
- icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n",
- source_nelems, source_vmlist[0], source_vmlist[1],
- source_vmlist[2], dest_vmids[0]);
-out:
- return ret;
-}
-
-static int icnss_setup_msa_permissions(struct icnss_priv *priv)
-{
- int ret;
- int i;
-
- if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
- return 0;
-
- for (i = 0; i < priv->nr_mem_region; i++) {
-
- ret = icnss_map_msa_permissions(&priv->mem_region[i]);
- if (ret)
- goto err_unmap;
- }
-
- set_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
-
- return 0;
-
-err_unmap:
- for (i--; i >= 0; i--)
- icnss_unmap_msa_permissions(&priv->mem_region[i]);
- return ret;
-}
-
-static void icnss_remove_msa_permissions(struct icnss_priv *priv)
-{
- int i;
-
- if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
- return;
-
- for (i = 0; i < priv->nr_mem_region; i++)
- icnss_unmap_msa_permissions(&priv->mem_region[i]);
-
- clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
-}
-
static int wlfw_msa_mem_info_send_sync_msg(void)
{
int ret;
@@ -1912,9 +1933,12 @@ static int icnss_driver_event_server_arrive(void *data)
if (ret < 0)
goto err_power_on;
- ret = icnss_setup_msa_permissions(penv);
- if (ret < 0)
- goto err_power_on;
+ if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
+ ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
+ if (ret < 0)
+ goto err_power_on;
+ set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
+ }
ret = wlfw_msa_ready_send_sync_msg();
if (ret < 0)
@@ -1932,7 +1956,7 @@ static int icnss_driver_event_server_arrive(void *data)
return ret;
err_setup_msa:
- icnss_remove_msa_permissions(penv);
+ icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
err_power_on:
icnss_hw_power_off(penv);
fail:
@@ -2347,21 +2371,29 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
struct icnss_priv *priv = container_of(nb, struct icnss_priv,
modem_ssr_nb);
struct icnss_uevent_fw_down_data fw_down_data;
+ int ret = 0;
icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
- if (code == SUBSYS_AFTER_SHUTDOWN &&
- notif->crashed == CRASH_STATUS_ERR_FATAL) {
- icnss_remove_msa_permissions(priv);
- icnss_pr_info("Collecting msa0 segment dump\n");
- icnss_msa0_ramdump(priv);
+ if (code == SUBSYS_AFTER_SHUTDOWN) {
+ ret = icnss_assign_msa_perm_all(priv,
+ ICNSS_MSA_PERM_DUMP_COLLECT);
+ if (!ret) {
+ icnss_pr_info("Collecting msa0 segment dump\n");
+ icnss_msa0_ramdump(priv);
+ icnss_assign_msa_perm_all(priv,
+ ICNSS_MSA_PERM_WLAN_HW_RW);
+ } else {
+ icnss_pr_err("Not able to Collect msa0 segment dump"
+ "Apps permissions not assigned %d\n", ret);
+ }
return NOTIFY_OK;
}
if (code != SUBSYS_BEFORE_SHUTDOWN)
return NOTIFY_OK;
- if (test_bit(ICNSS_PDR_ENABLED, &priv->state))
+ if (test_bit(ICNSS_PDR_REGISTERED, &priv->state))
return NOTIFY_OK;
icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
@@ -2402,14 +2434,14 @@ static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
icnss_pr_err("Modem register notifier failed: %d\n", ret);
}
- set_bit(ICNSS_SSR_ENABLED, &priv->state);
+ set_bit(ICNSS_SSR_REGISTERED, &priv->state);
return ret;
}
static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
{
- if (!test_and_clear_bit(ICNSS_SSR_ENABLED, &priv->state))
+ if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
return 0;
subsys_notif_unregister_notifier(priv->modem_notify_handler,
@@ -2423,7 +2455,7 @@ static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
{
int i;
- if (!test_and_clear_bit(ICNSS_PDR_ENABLED, &priv->state))
+ if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
return 0;
for (i = 0; i < priv->total_domains; i++)
@@ -2547,9 +2579,10 @@ static int icnss_get_service_location_notify(struct notifier_block *nb,
priv->service_notifier = notifier;
priv->total_domains = pd->total_domains;
- set_bit(ICNSS_PDR_ENABLED, &priv->state);
+ set_bit(ICNSS_PDR_REGISTERED, &priv->state);
- icnss_pr_dbg("PD restart enabled, state: 0x%lx\n", priv->state);
+ icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
+ priv->state);
return NOTIFY_OK;
@@ -3204,7 +3237,7 @@ int icnss_trigger_recovery(struct device *dev)
goto out;
}
- if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) {
+ if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
priv->state);
ret = -EOPNOTSUPP;
@@ -3658,11 +3691,11 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
case ICNSS_PM_SUSPEND_NOIRQ:
seq_puts(s, "PM SUSPEND NOIRQ");
continue;
- case ICNSS_SSR_ENABLED:
- seq_puts(s, "SSR ENABLED");
+ case ICNSS_SSR_REGISTERED:
+ seq_puts(s, "SSR REGISTERED");
continue;
- case ICNSS_PDR_ENABLED:
- seq_puts(s, "PDR ENABLED");
+ case ICNSS_PDR_REGISTERED:
+ seq_puts(s, "PDR REGISTERED");
continue;
case ICNSS_PD_RESTART:
seq_puts(s, "PD RESTART");
@@ -4276,6 +4309,11 @@ static int icnss_probe(struct platform_device *pdev)
icnss_debugfs_create(priv);
+ ret = device_init_wakeup(&priv->pdev->dev, true);
+ if (ret)
+ icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
+ ret);
+
penv = priv;
icnss_pr_info("Platform driver probed successfully\n");
@@ -4296,6 +4334,8 @@ static int icnss_remove(struct platform_device *pdev)
{
icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
+ device_init_wakeup(&penv->pdev->dev, false);
+
icnss_debugfs_destroy(penv);
icnss_modem_ssr_unregister_notifier(penv);
@@ -4313,7 +4353,8 @@ static int icnss_remove(struct platform_device *pdev)
icnss_hw_power_off(penv);
- icnss_remove_msa_permissions(penv);
+ icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
+ clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
dev_set_drvdata(&pdev->dev, NULL);
diff --git a/drivers/soc/qcom/icnss_utils.c b/drivers/soc/qcom/icnss_utils.c
index a7a0ffa2c18e..ca22b6cdf4a1 100644
--- a/drivers/soc/qcom/icnss_utils.c
+++ b/drivers/soc/qcom/icnss_utils.c
@@ -12,11 +12,14 @@
#include <linux/module.h>
#include <linux/slab.h>
+#include <soc/qcom/icnss.h>
#define ICNSS_MAX_CH_NUM 45
static DEFINE_MUTEX(unsafe_channel_list_lock);
static DEFINE_SPINLOCK(dfs_nol_info_lock);
+static int driver_load_cnt;
+static enum cnss_cc_src icnss_cc_source = CNSS_SOURCE_CORE;
static struct icnss_unsafe_channel_list {
u16 unsafe_ch_count;
@@ -124,3 +127,28 @@ int icnss_wlan_get_dfs_nol(void *info, u16 info_len)
return len;
}
EXPORT_SYMBOL(icnss_wlan_get_dfs_nol);
+
+void icnss_increment_driver_load_cnt(void)
+{
+ ++driver_load_cnt;
+}
+EXPORT_SYMBOL(icnss_increment_driver_load_cnt);
+
+int icnss_get_driver_load_cnt(void)
+{
+ return driver_load_cnt;
+}
+EXPORT_SYMBOL(icnss_get_driver_load_cnt);
+
+
+void icnss_set_cc_source(enum cnss_cc_src cc_source)
+{
+ icnss_cc_source = cc_source;
+}
+EXPORT_SYMBOL(icnss_set_cc_source);
+
+enum cnss_cc_src icnss_get_cc_source(void)
+{
+ return icnss_cc_source;
+}
+EXPORT_SYMBOL(icnss_get_cc_source);
diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
index 7406dba44320..6dd4b06bf377 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.c
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -968,8 +968,8 @@ static int memshare_child_probe(struct platform_device *pdev)
/*
* Memshare allocation for guaranteed clients
*/
- if (memblock[num_clients].guarantee) {
- if (client_id == 1 && size > 0)
+ if (memblock[num_clients].guarantee && size > 0) {
+ if (client_id == 1)
size += MEMSHARE_GUARD_BYTES;
rc = memshare_alloc(memsh_child->dev,
size,
@@ -980,6 +980,7 @@ static int memshare_child_probe(struct platform_device *pdev)
return rc;
}
memblock[num_clients].alloted = 1;
+ shared_hyp_mapping(num_clients);
}
/*
diff --git a/drivers/soundwire/soundwire.c b/drivers/soundwire/soundwire.c
index 6691418b516e..63545651fe43 100755
--- a/drivers/soundwire/soundwire.c
+++ b/drivers/soundwire/soundwire.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
@@ -68,6 +68,27 @@ static void swr_dev_release(struct device *dev)
}
/**
+ * swr_remove_device - remove a soundwire device
+ * @swr_dev: soundwire device to remove
+ *
+ * Remove a soundwire device. Go through the soundwire
+ * device list that master has and remove swr_dev from
+ * it.
+ */
+void swr_remove_device(struct swr_device *swr_dev)
+{
+ struct swr_device *swr_dev_loop, *safe;
+
+ list_for_each_entry_safe(swr_dev_loop, safe,
+ &swr_dev->master->devices,
+ dev_list) {
+ if (swr_dev == swr_dev_loop)
+ list_del(&swr_dev_loop->dev_list);
+ }
+}
+EXPORT_SYMBOL(swr_remove_device);
+
+/**
* swr_new_device - instantiate a new soundwire device
* @master: Controller to which device is connected
* @info: Describes the soundwire device
@@ -129,47 +150,6 @@ err_out:
EXPORT_SYMBOL(swr_new_device);
/**
- * swr_startup_devices - perform additional initialization for child devices
- *
- * @swr_dev: pointer to soundwire slave device
- *
- * Performs any additional initialization needed for a soundwire slave device.
- * This is a optional functionality defined by slave devices.
- * Removes the slave node from the list, in case there is any failure.
- */
-int swr_startup_devices(struct swr_device *swr_dev)
-{
- struct swr_driver *swr_drv;
- struct device *dev;
- int ret = 0;
-
- if (!swr_dev)
- return -EINVAL;
-
- dev = &swr_dev->dev;
- if (!dev)
- return -EINVAL;
-
- swr_drv = to_swr_driver(dev->driver);
- if (!swr_drv)
- return -EINVAL;
-
- if (swr_drv->startup) {
- ret = swr_drv->startup(swr_dev);
- if (ret)
- goto out;
-
- dev_dbg(&swr_dev->dev,
- "%s: startup complete for device %lx\n",
- __func__, swr_dev->addr);
- }
-
-out:
- return ret;
-}
-EXPORT_SYMBOL(swr_startup_devices);
-
-/**
* of_register_swr_devices - register child devices on to the soundwire bus
* @master: pointer to soundwire master device
*
@@ -203,14 +183,15 @@ int of_register_swr_devices(struct swr_master *master)
}
info.addr = addr;
info.of_node = of_node_get(node);
+ master->num_dev++;
swr = swr_new_device(master, &info);
if (!swr) {
dev_err(&master->dev, "of_swr: Register failed %s\n",
node->full_name);
of_node_put(node);
+ master->num_dev--;
continue;
}
- master->num_dev++;
}
return 0;
}
@@ -610,7 +591,7 @@ int swr_device_up(struct swr_device *swr_dev)
dev = &swr_dev->dev;
sdrv = to_swr_driver(dev->driver);
if (!sdrv)
- return -EINVAL;
+ return 0;
if (sdrv->device_up)
return sdrv->device_up(to_swr_device(dev));
@@ -638,7 +619,7 @@ int swr_device_down(struct swr_device *swr_dev)
dev = &swr_dev->dev;
sdrv = to_swr_driver(dev->driver);
if (!sdrv)
- return -EINVAL;
+ return 0;
if (sdrv->device_down)
return sdrv->device_down(to_swr_device(dev));
diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
index 14c13db991a1..cdaf009c5b1f 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -1369,7 +1369,6 @@ static int swrm_probe(struct platform_device *pdev)
{
struct swr_mstr_ctrl *swrm;
struct swr_ctrl_platform_data *pdata;
- struct swr_device *swr_dev, *safe;
int ret;
/* Allocate soundwire master driver structure */
@@ -1470,9 +1469,6 @@ static int swrm_probe(struct platform_device *pdev)
goto err_mstr_fail;
}
- if (pdev->dev.of_node)
- of_register_swr_devices(&swrm->master);
-
/* Add devices registered with board-info as the
controller will be up now
*/
@@ -1489,15 +1485,11 @@ static int swrm_probe(struct platform_device *pdev)
}
swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION);
- /* Enumerate slave devices */
- list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices,
- dev_list) {
- ret = swr_startup_devices(swr_dev);
- if (ret)
- list_del(&swr_dev->dev_list);
- }
mutex_unlock(&swrm->mlock);
+ if (pdev->dev.of_node)
+ of_register_swr_devices(&swrm->master);
+
dbgswrm = swrm;
debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0);
if (!IS_ERR(debugfs_swrm_dent)) {
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 1de3dddfd0f4..8deee007218b 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -429,6 +429,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
global_page_state(NR_FILE_PAGES) + zcache_pages())
other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() -
global_page_state(NR_SHMEM) -
+ global_page_state(NR_UNEVICTABLE) -
total_swapcache_pages();
else
other_file = 0;
diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c
index b87192e0f9aa..109becdabc24 100644
--- a/drivers/staging/comedi/drivers/jr3_pci.c
+++ b/drivers/staging/comedi/drivers/jr3_pci.c
@@ -610,7 +610,7 @@ static void jr3_pci_poll_dev(unsigned long data)
s = &dev->subdevices[i];
spriv = s->private;
- if (now > spriv->next_time_min) {
+ if (time_after_eq(now, spriv->next_time_min)) {
struct jr3_pci_poll_delay sub_delay;
sub_delay = jr3_pci_poll_subdevice(s);
@@ -726,11 +726,12 @@ static int jr3_pci_auto_attach(struct comedi_device *dev,
s->insn_read = jr3_pci_ai_insn_read;
spriv = jr3_pci_alloc_spriv(dev, s);
- if (spriv) {
- /* Channel specific range and maxdata */
- s->range_table_list = spriv->range_table_list;
- s->maxdata_list = spriv->maxdata_list;
- }
+ if (!spriv)
+ return -ENOMEM;
+
+ /* Channel specific range and maxdata */
+ s->range_table_list = spriv->range_table_list;
+ s->maxdata_list = spriv->maxdata_list;
}
/* Reset DSP card */
diff --git a/drivers/staging/gdm724x/gdm_mux.c b/drivers/staging/gdm724x/gdm_mux.c
index 445f83615575..fb4f3fea6c66 100644
--- a/drivers/staging/gdm724x/gdm_mux.c
+++ b/drivers/staging/gdm724x/gdm_mux.c
@@ -670,14 +670,14 @@ static int __init gdm_usb_mux_init(void)
static void __exit gdm_usb_mux_exit(void)
{
- unregister_lte_tty_driver();
-
if (mux_rx_wq) {
flush_workqueue(mux_rx_wq);
destroy_workqueue(mux_rx_wq);
}
usb_deregister(&gdm_mux_driver);
+ unregister_lte_tty_driver();
+
}
module_init(gdm_usb_mux_init);
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
index e9c4f973bba9..79bf13f5c0d1 100644
--- a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
@@ -97,8 +97,9 @@ void rtl92e_set_reg(struct net_device *dev, u8 variable, u8 *val)
switch (variable) {
case HW_VAR_BSSID:
- rtl92e_writel(dev, BSSIDR, ((u32 *)(val))[0]);
- rtl92e_writew(dev, BSSIDR+2, ((u16 *)(val+2))[0]);
+ /* BSSIDR 2 byte alignment */
+ rtl92e_writew(dev, BSSIDR, *(u16 *)val);
+ rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(val + 2));
break;
case HW_VAR_MEDIA_STATUS:
@@ -626,7 +627,7 @@ void rtl92e_get_eeprom_size(struct net_device *dev)
struct r8192_priv *priv = rtllib_priv(dev);
RT_TRACE(COMP_INIT, "===========>%s()\n", __func__);
- curCR = rtl92e_readl(dev, EPROM_CMD);
+ curCR = rtl92e_readw(dev, EPROM_CMD);
RT_TRACE(COMP_INIT, "read from Reg Cmd9346CR(%x):%x\n", EPROM_CMD,
curCR);
priv->epromtype = (curCR & EPROM_CMD_9356SEL) ? EEPROM_93C56 :
@@ -963,8 +964,8 @@ static void _rtl92e_net_update(struct net_device *dev)
rtl92e_config_rate(dev, &rate_config);
priv->dot11CurrentPreambleMode = PREAMBLE_AUTO;
priv->basic_rate = rate_config &= 0x15f;
- rtl92e_writel(dev, BSSIDR, ((u32 *)net->bssid)[0]);
- rtl92e_writew(dev, BSSIDR+4, ((u16 *)net->bssid)[2]);
+ rtl92e_writew(dev, BSSIDR, *(u16 *)net->bssid);
+ rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(net->bssid + 2));
if (priv->rtllib->iw_mode == IW_MODE_ADHOC) {
rtl92e_writew(dev, ATIMWND, 2);
diff --git a/drivers/staging/vt6656/usbpipe.c b/drivers/staging/vt6656/usbpipe.c
index c975c3b87093..cfc3017fd64a 100644
--- a/drivers/staging/vt6656/usbpipe.c
+++ b/drivers/staging/vt6656/usbpipe.c
@@ -50,15 +50,25 @@ int vnt_control_out(struct vnt_private *priv, u8 request, u16 value,
u16 index, u16 length, u8 *buffer)
{
int status = 0;
+ u8 *usb_buffer;
if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags))
return STATUS_FAILURE;
mutex_lock(&priv->usb_lock);
+ usb_buffer = kmemdup(buffer, length, GFP_KERNEL);
+ if (!usb_buffer) {
+ mutex_unlock(&priv->usb_lock);
+ return -ENOMEM;
+ }
+
status = usb_control_msg(priv->usb,
- usb_sndctrlpipe(priv->usb, 0), request, 0x40, value,
- index, buffer, length, USB_CTL_WAIT);
+ usb_sndctrlpipe(priv->usb, 0),
+ request, 0x40, value,
+ index, usb_buffer, length, USB_CTL_WAIT);
+
+ kfree(usb_buffer);
mutex_unlock(&priv->usb_lock);
@@ -78,15 +88,28 @@ int vnt_control_in(struct vnt_private *priv, u8 request, u16 value,
u16 index, u16 length, u8 *buffer)
{
int status;
+ u8 *usb_buffer;
if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags))
return STATUS_FAILURE;
mutex_lock(&priv->usb_lock);
+ usb_buffer = kmalloc(length, GFP_KERNEL);
+ if (!usb_buffer) {
+ mutex_unlock(&priv->usb_lock);
+ return -ENOMEM;
+ }
+
status = usb_control_msg(priv->usb,
- usb_rcvctrlpipe(priv->usb, 0), request, 0xc0, value,
- index, buffer, length, USB_CTL_WAIT);
+ usb_rcvctrlpipe(priv->usb, 0),
+ request, 0xc0, value,
+ index, usb_buffer, length, USB_CTL_WAIT);
+
+ if (status == length)
+ memcpy(buffer, usb_buffer, length);
+
+ kfree(usb_buffer);
mutex_unlock(&priv->usb_lock);
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 6ed80b05d674..200d3de8bc1e 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -4821,6 +4821,7 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
continue;
}
atomic_set(&sess->session_reinstatement, 1);
+ atomic_set(&sess->session_fall_back_to_erl0, 1);
spin_unlock(&sess->conn_lock);
list_move_tail(&se_sess->sess_list, &free_list);
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index b4bfd706ac94..dc1bd1f1bdfe 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -725,11 +725,8 @@ static ssize_t lio_target_nacl_cmdsn_depth_store(struct config_item *item,
if (iscsit_get_tpg(tpg) < 0)
return -EINVAL;
- /*
- * iscsit_tpg_set_initiator_node_queue_depth() assumes force=1
- */
- ret = iscsit_tpg_set_initiator_node_queue_depth(tpg,
- config_item_name(acl_ci), cmdsn_depth, 1);
+
+ ret = core_tpg_set_initiator_node_queue_depth(se_nacl, cmdsn_depth);
pr_debug("LIO_Target_ConfigFS: %s/%s Set CmdSN Window: %u for"
"InitiatorName: %s\n", config_item_name(wwn_ci),
@@ -1593,42 +1590,31 @@ static int lio_tpg_check_prot_fabric_only(
}
/*
- * Called with spin_lock_irq(struct se_portal_group->session_lock) held
- * or not held.
- *
- * Also, this function calls iscsit_inc_session_usage_count() on the
+ * This function calls iscsit_inc_session_usage_count() on the
* struct iscsi_session in question.
*/
static int lio_tpg_shutdown_session(struct se_session *se_sess)
{
struct iscsi_session *sess = se_sess->fabric_sess_ptr;
- struct se_portal_group *se_tpg = se_sess->se_tpg;
- bool local_lock = false;
-
- if (!spin_is_locked(&se_tpg->session_lock)) {
- spin_lock_irq(&se_tpg->session_lock);
- local_lock = true;
- }
+ struct se_portal_group *se_tpg = &sess->tpg->tpg_se_tpg;
+ spin_lock_bh(&se_tpg->session_lock);
spin_lock(&sess->conn_lock);
if (atomic_read(&sess->session_fall_back_to_erl0) ||
atomic_read(&sess->session_logout) ||
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
spin_unlock(&sess->conn_lock);
- if (local_lock)
- spin_unlock_irq(&sess->conn_lock);
+ spin_unlock_bh(&se_tpg->session_lock);
return 0;
}
atomic_set(&sess->session_reinstatement, 1);
+ atomic_set(&sess->session_fall_back_to_erl0, 1);
spin_unlock(&sess->conn_lock);
iscsit_stop_time2retain_timer(sess);
- spin_unlock_irq(&se_tpg->session_lock);
+ spin_unlock_bh(&se_tpg->session_lock);
iscsit_stop_session(sess, 1, 1);
- if (!local_lock)
- spin_lock_irq(&se_tpg->session_lock);
-
return 1;
}
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 316f66172335..4a137b0ae3dc 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -195,6 +195,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
initiatorname_param->value) &&
(sess_p->sess_ops->SessionType == sessiontype))) {
atomic_set(&sess_p->session_reinstatement, 1);
+ atomic_set(&sess_p->session_fall_back_to_erl0, 1);
spin_unlock(&sess_p->conn_lock);
iscsit_inc_session_usage_count(sess_p);
iscsit_stop_time2retain_timer(sess_p);
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index 68261b7dcefe..205a509b0dfb 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -589,16 +589,6 @@ int iscsit_tpg_del_network_portal(
return iscsit_tpg_release_np(tpg_np, tpg, np);
}
-int iscsit_tpg_set_initiator_node_queue_depth(
- struct iscsi_portal_group *tpg,
- unsigned char *initiatorname,
- u32 queue_depth,
- int force)
-{
- return core_tpg_set_initiator_node_queue_depth(&tpg->tpg_se_tpg,
- initiatorname, queue_depth, force);
-}
-
int iscsit_ta_authentication(struct iscsi_portal_group *tpg, u32 authentication)
{
unsigned char buf1[256], buf2[256], *none = NULL;
diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h
index 9db32bd24cd4..2da211920c18 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.h
+++ b/drivers/target/iscsi/iscsi_target_tpg.h
@@ -26,8 +26,6 @@ extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_gr
int);
extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *,
struct iscsi_tpg_np *);
-extern int iscsit_tpg_set_initiator_node_queue_depth(struct iscsi_portal_group *,
- unsigned char *, u32, int);
extern int iscsit_ta_authentication(struct iscsi_portal_group *, u32);
extern int iscsit_ta_login_timeout(struct iscsi_portal_group *, u32);
extern int iscsit_ta_netif_timeout(struct iscsi_portal_group *, u32);
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 79291869bce6..041a56987845 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -594,8 +594,7 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
if (ret < 0)
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- if (ret)
- target_complete_cmd(cmd, SAM_STAT_GOOD);
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
return 0;
}
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 90c5dffc9fa4..608117819366 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -498,8 +498,11 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
* been failed with a non-zero SCSI status.
*/
if (cmd->scsi_status) {
- pr_err("compare_and_write_callback: non zero scsi_status:"
+ pr_debug("compare_and_write_callback: non zero scsi_status:"
" 0x%02x\n", cmd->scsi_status);
+ *post_ret = 1;
+ if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION)
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
goto out;
}
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 2794c6ec5c3c..899c33b3c734 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -169,28 +169,25 @@ void core_tpg_add_node_to_devs(
mutex_unlock(&tpg->tpg_lun_mutex);
}
-/* core_set_queue_depth_for_node():
- *
- *
- */
-static int core_set_queue_depth_for_node(
- struct se_portal_group *tpg,
- struct se_node_acl *acl)
+static void
+target_set_nacl_queue_depth(struct se_portal_group *tpg,
+ struct se_node_acl *acl, u32 queue_depth)
{
+ acl->queue_depth = queue_depth;
+
if (!acl->queue_depth) {
- pr_err("Queue depth for %s Initiator Node: %s is 0,"
+ pr_warn("Queue depth for %s Initiator Node: %s is 0,"
"defaulting to 1.\n", tpg->se_tpg_tfo->get_fabric_name(),
acl->initiatorname);
acl->queue_depth = 1;
}
-
- return 0;
}
static struct se_node_acl *target_alloc_node_acl(struct se_portal_group *tpg,
const unsigned char *initiatorname)
{
struct se_node_acl *acl;
+ u32 queue_depth;
acl = kzalloc(max(sizeof(*acl), tpg->se_tpg_tfo->node_acl_size),
GFP_KERNEL);
@@ -205,24 +202,20 @@ static struct se_node_acl *target_alloc_node_acl(struct se_portal_group *tpg,
spin_lock_init(&acl->nacl_sess_lock);
mutex_init(&acl->lun_entry_mutex);
atomic_set(&acl->acl_pr_ref_count, 0);
+
if (tpg->se_tpg_tfo->tpg_get_default_depth)
- acl->queue_depth = tpg->se_tpg_tfo->tpg_get_default_depth(tpg);
+ queue_depth = tpg->se_tpg_tfo->tpg_get_default_depth(tpg);
else
- acl->queue_depth = 1;
+ queue_depth = 1;
+ target_set_nacl_queue_depth(tpg, acl, queue_depth);
+
snprintf(acl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname);
acl->se_tpg = tpg;
acl->acl_index = scsi_get_new_index(SCSI_AUTH_INTR_INDEX);
tpg->se_tpg_tfo->set_default_node_attributes(acl);
- if (core_set_queue_depth_for_node(tpg, acl) < 0)
- goto out_free_acl;
-
return acl;
-
-out_free_acl:
- kfree(acl);
- return NULL;
}
static void target_add_node_acl(struct se_node_acl *acl)
@@ -369,7 +362,8 @@ void core_tpg_del_initiator_node_acl(struct se_node_acl *acl)
if (sess->sess_tearing_down != 0)
continue;
- target_get_session(sess);
+ if (!target_get_session(sess))
+ continue;
list_move(&sess->sess_acl_list, &sess_list);
}
spin_unlock_irqrestore(&acl->nacl_sess_lock, flags);
@@ -406,108 +400,52 @@ void core_tpg_del_initiator_node_acl(struct se_node_acl *acl)
*
*/
int core_tpg_set_initiator_node_queue_depth(
- struct se_portal_group *tpg,
- unsigned char *initiatorname,
- u32 queue_depth,
- int force)
+ struct se_node_acl *acl,
+ u32 queue_depth)
{
- struct se_session *sess, *init_sess = NULL;
- struct se_node_acl *acl;
+ LIST_HEAD(sess_list);
+ struct se_portal_group *tpg = acl->se_tpg;
+ struct se_session *sess, *sess_tmp;
unsigned long flags;
- int dynamic_acl = 0;
-
- mutex_lock(&tpg->acl_node_mutex);
- acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
- if (!acl) {
- pr_err("Access Control List entry for %s Initiator"
- " Node %s does not exists for TPG %hu, ignoring"
- " request.\n", tpg->se_tpg_tfo->get_fabric_name(),
- initiatorname, tpg->se_tpg_tfo->tpg_get_tag(tpg));
- mutex_unlock(&tpg->acl_node_mutex);
- return -ENODEV;
- }
- if (acl->dynamic_node_acl) {
- acl->dynamic_node_acl = 0;
- dynamic_acl = 1;
- }
- mutex_unlock(&tpg->acl_node_mutex);
-
- spin_lock_irqsave(&tpg->session_lock, flags);
- list_for_each_entry(sess, &tpg->tpg_sess_list, sess_list) {
- if (sess->se_node_acl != acl)
- continue;
-
- if (!force) {
- pr_err("Unable to change queue depth for %s"
- " Initiator Node: %s while session is"
- " operational. To forcefully change the queue"
- " depth and force session reinstatement"
- " use the \"force=1\" parameter.\n",
- tpg->se_tpg_tfo->get_fabric_name(), initiatorname);
- spin_unlock_irqrestore(&tpg->session_lock, flags);
-
- mutex_lock(&tpg->acl_node_mutex);
- if (dynamic_acl)
- acl->dynamic_node_acl = 1;
- mutex_unlock(&tpg->acl_node_mutex);
- return -EEXIST;
- }
- /*
- * Determine if the session needs to be closed by our context.
- */
- if (!tpg->se_tpg_tfo->shutdown_session(sess))
- continue;
-
- init_sess = sess;
- break;
- }
+ int rc;
/*
* User has requested to change the queue depth for a Initiator Node.
* Change the value in the Node's struct se_node_acl, and call
- * core_set_queue_depth_for_node() to add the requested queue depth.
- *
- * Finally call tpg->se_tpg_tfo->close_session() to force session
- * reinstatement to occur if there is an active session for the
- * $FABRIC_MOD Initiator Node in question.
+ * target_set_nacl_queue_depth() to set the new queue depth.
*/
- acl->queue_depth = queue_depth;
+ target_set_nacl_queue_depth(tpg, acl, queue_depth);
+
+ spin_lock_irqsave(&acl->nacl_sess_lock, flags);
+ list_for_each_entry_safe(sess, sess_tmp, &acl->acl_sess_list,
+ sess_acl_list) {
+ if (sess->sess_tearing_down != 0)
+ continue;
+ if (!target_get_session(sess))
+ continue;
+ spin_unlock_irqrestore(&acl->nacl_sess_lock, flags);
- if (core_set_queue_depth_for_node(tpg, acl) < 0) {
- spin_unlock_irqrestore(&tpg->session_lock, flags);
/*
- * Force session reinstatement if
- * core_set_queue_depth_for_node() failed, because we assume
- * the $FABRIC_MOD has already the set session reinstatement
- * bit from tpg->se_tpg_tfo->shutdown_session() called above.
+ * Finally call tpg->se_tpg_tfo->close_session() to force session
+ * reinstatement to occur if there is an active session for the
+ * $FABRIC_MOD Initiator Node in question.
*/
- if (init_sess)
- tpg->se_tpg_tfo->close_session(init_sess);
-
- mutex_lock(&tpg->acl_node_mutex);
- if (dynamic_acl)
- acl->dynamic_node_acl = 1;
- mutex_unlock(&tpg->acl_node_mutex);
- return -EINVAL;
+ rc = tpg->se_tpg_tfo->shutdown_session(sess);
+ target_put_session(sess);
+ if (!rc) {
+ spin_lock_irqsave(&acl->nacl_sess_lock, flags);
+ continue;
+ }
+ target_put_session(sess);
+ spin_lock_irqsave(&acl->nacl_sess_lock, flags);
}
- spin_unlock_irqrestore(&tpg->session_lock, flags);
- /*
- * If the $FABRIC_MOD session for the Initiator Node ACL exists,
- * forcefully shutdown the $FABRIC_MOD session/nexus.
- */
- if (init_sess)
- tpg->se_tpg_tfo->close_session(init_sess);
+ spin_unlock_irqrestore(&acl->nacl_sess_lock, flags);
pr_debug("Successfully changed queue depth to: %d for Initiator"
- " Node: %s on %s Target Portal Group: %u\n", queue_depth,
- initiatorname, tpg->se_tpg_tfo->get_fabric_name(),
+ " Node: %s on %s Target Portal Group: %u\n", acl->queue_depth,
+ acl->initiatorname, tpg->se_tpg_tfo->get_fabric_name(),
tpg->se_tpg_tfo->tpg_get_tag(tpg));
- mutex_lock(&tpg->acl_node_mutex);
- if (dynamic_acl)
- acl->dynamic_node_acl = 1;
- mutex_unlock(&tpg->acl_node_mutex);
-
return 0;
}
EXPORT_SYMBOL(core_tpg_set_initiator_node_queue_depth);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index df2059984e14..af301414a9f3 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -383,9 +383,9 @@ static void target_release_session(struct kref *kref)
se_tpg->se_tpg_tfo->close_session(se_sess);
}
-void target_get_session(struct se_session *se_sess)
+int target_get_session(struct se_session *se_sess)
{
- kref_get(&se_sess->sess_kref);
+ return kref_get_unless_zero(&se_sess->sess_kref);
}
EXPORT_SYMBOL(target_get_session);
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index c8cbd078bb07..b1f4c3f27111 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -7409,11 +7409,11 @@ static int msm_thermal_dev_probe(struct platform_device *pdev)
pr_err("thermal pre init failed. err:%d\n", ret);
goto probe_exit;
}
+ probe_sensor_info(node, &data, pdev);
ret = probe_deferrable_properties(node, &data, pdev);
if (ret)
goto probe_exit;
- probe_sensor_info(node, &data, pdev);
probe_cc(node, &data, pdev);
probe_freq_mitigation(node, &data, pdev);
probe_cx_phase_ctrl(node, &data, pdev);
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 807d80145686..96aa0ad32497 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -216,16 +216,11 @@ static int pty_signal(struct tty_struct *tty, int sig)
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
- struct tty_ldisc *ld;
if (!to)
return;
- ld = tty_ldisc_ref(to);
- tty_buffer_flush(to, ld);
- if (ld)
- tty_ldisc_deref(ld);
-
+ tty_buffer_flush(to, NULL);
if (to->packet) {
spin_lock_irq(&tty->ctrl_lock);
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 24280d9a05e9..de1c143b475f 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -1712,7 +1712,8 @@ static int serial_omap_probe(struct platform_device *pdev)
return 0;
err_add_port:
- pm_runtime_put(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_qos_remove_request(&up->pm_qos_request);
device_init_wakeup(up->dev, false);
@@ -1725,9 +1726,13 @@ static int serial_omap_remove(struct platform_device *dev)
{
struct uart_omap_port *up = platform_get_drvdata(dev);
+ pm_runtime_get_sync(up->dev);
+
+ uart_remove_one_port(&serial_omap_reg, &up->port);
+
+ pm_runtime_dont_use_autosuspend(up->dev);
pm_runtime_put_sync(up->dev);
pm_runtime_disable(up->dev);
- uart_remove_one_port(&serial_omap_reg, &up->port);
pm_qos_remove_request(&up->pm_qos_request);
device_init_wakeup(&dev->dev, false);
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index cad76b1cf672..df642496f989 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -900,14 +900,13 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
return -ENOMEM;
}
- dma->rx_addr = dma_map_single(dma->rx_chan->device->dev, dma->rx_buf,
+ dma->rx_addr = dma_map_single(p->port.dev, dma->rx_buf,
dma->rx_size, DMA_FROM_DEVICE);
spin_lock_irqsave(&p->port.lock, flags);
/* TX buffer */
- dma->tx_addr = dma_map_single(dma->tx_chan->device->dev,
- p->port.state->xmit.buf,
+ dma->tx_addr = dma_map_single(p->port.dev, p->port.state->xmit.buf,
UART_XMIT_SIZE, DMA_TO_DEVICE);
spin_unlock_irqrestore(&p->port.lock, flags);
@@ -921,7 +920,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
if (dma->rx_chan) {
dmaengine_terminate_all(dma->rx_chan);
- dma_unmap_single(dma->rx_chan->device->dev, dma->rx_addr,
+ dma_unmap_single(p->port.dev, dma->rx_addr,
dma->rx_size, DMA_FROM_DEVICE);
kfree(dma->rx_buf);
dma_release_channel(dma->rx_chan);
@@ -930,7 +929,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
if (dma->tx_chan) {
dmaengine_terminate_all(dma->tx_chan);
- dma_unmap_single(dma->tx_chan->device->dev, dma->tx_addr,
+ dma_unmap_single(p->port.dev, dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
dma_release_channel(dma->tx_chan);
dma->tx_chan = NULL;
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 96849e2e7435..0b7194086c5a 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -311,6 +311,12 @@ static void acm_ctrl_irq(struct urb *urb)
break;
case USB_CDC_NOTIFY_SERIAL_STATE:
+ if (le16_to_cpu(dr->wLength) != 2) {
+ dev_dbg(&acm->control->dev,
+ "%s - malformed serial state\n", __func__);
+ break;
+ }
+
newctrl = get_unaligned_le16(data);
if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
@@ -347,11 +353,10 @@ static void acm_ctrl_irq(struct urb *urb)
default:
dev_dbg(&acm->control->dev,
- "%s - unknown notification %d received: index %d "
- "len %d data0 %d data1 %d\n",
+ "%s - unknown notification %d received: index %d len %d\n",
__func__,
- dr->bNotificationType, dr->wIndex,
- dr->wLength, data[0], data[1]);
+ dr->bNotificationType, dr->wIndex, dr->wLength);
+
break;
}
exit:
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 26a305f43ff6..ee33c0d796b5 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1328,6 +1328,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*/
if (udev->parent && !PMSG_IS_AUTO(msg))
status = 0;
+
+ /*
+ * If the device is inaccessible, don't try to resume
+ * suspended interfaces and just return the error.
+ */
+ if (status && status != -EBUSY) {
+ int err;
+ u16 devstat;
+
+ err = usb_get_status(udev, USB_RECIP_DEVICE, 0,
+ &devstat);
+ if (err) {
+ dev_err(&udev->dev,
+ "Failed to suspend device, error %d\n",
+ status);
+ goto done;
+ }
+ }
}
/* If the suspend failed, resume interfaces that did get suspended */
@@ -1772,6 +1790,9 @@ static int autosuspend_check(struct usb_device *udev)
int w, i;
struct usb_interface *intf;
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+
/* Fail if autosuspend is disabled, or any interfaces are in use, or
* any interface drivers require remote wakeup but it isn't available.
*/
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index ea337a718cc1..b3de806085f0 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -26,6 +26,7 @@
#define MAX_USB_MINORS 256
static const struct file_operations *usb_minors[MAX_USB_MINORS];
static DECLARE_RWSEM(minor_rwsem);
+static DEFINE_MUTEX(init_usb_class_mutex);
static int usb_open(struct inode *inode, struct file *file)
{
@@ -108,8 +109,9 @@ static void release_usb_class(struct kref *kref)
static void destroy_usb_class(void)
{
- if (usb_class)
- kref_put(&usb_class->kref, release_usb_class);
+ mutex_lock(&init_usb_class_mutex);
+ kref_put(&usb_class->kref, release_usb_class);
+ mutex_unlock(&init_usb_class_mutex);
}
int usb_major_init(void)
@@ -171,7 +173,10 @@ int usb_register_dev(struct usb_interface *intf,
if (intf->minor >= 0)
return -EADDRINUSE;
+ mutex_lock(&init_usb_class_mutex);
retval = init_usb_class();
+ mutex_unlock(&init_usb_class_mutex);
+
if (retval)
return retval;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 87912ead87b7..a4efaecf85ef 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -363,7 +363,8 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
}
/* USB 2.0 spec Section 11.24.4.5 */
-static int get_hub_descriptor(struct usb_device *hdev, void *data)
+static int get_hub_descriptor(struct usb_device *hdev,
+ struct usb_hub_descriptor *desc)
{
int i, ret, size;
unsigned dtype;
@@ -379,10 +380,18 @@ static int get_hub_descriptor(struct usb_device *hdev, void *data)
for (i = 0; i < 3; i++) {
ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
- dtype << 8, 0, data, size,
+ dtype << 8, 0, desc, size,
USB_CTRL_GET_TIMEOUT);
- if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
+ if (hub_is_superspeed(hdev)) {
+ if (ret == size)
+ return ret;
+ } else if (ret >= USB_DT_HUB_NONVAR_SIZE + 2) {
+ /* Make sure we have the DeviceRemovable field. */
+ size = USB_DT_HUB_NONVAR_SIZE + desc->bNbrPorts / 8 + 1;
+ if (ret < size)
+ return -EMSGSIZE;
return ret;
+ }
}
return -EINVAL;
}
@@ -1059,6 +1068,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
portstatus = portchange = 0;
status = hub_port_status(hub, port1, &portstatus, &portchange);
+ if (status)
+ goto abort;
+
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
dev_dbg(&port_dev->dev, "status %04x change %04x\n",
portstatus, portchange);
@@ -1191,7 +1203,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/* Scan all ports that need attention */
kick_hub_wq(hub);
-
+ abort:
if (type == HUB_INIT2 || type == HUB_INIT3) {
/* Allow autosuspend if it was suppressed */
disconnected:
@@ -1303,7 +1315,7 @@ static int hub_configure(struct usb_hub *hub,
}
mutex_init(&hub->status_mutex);
- hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
+ hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
ret = -ENOMEM;
goto fail;
@@ -1311,7 +1323,7 @@ static int hub_configure(struct usb_hub *hub,
/* Request the entire hub descriptor.
* hub->descriptor can handle USB_MAXCHILDREN ports,
- * but the hub can/will return fewer bytes here.
+ * but a (non-SS) hub can/will return fewer bytes here.
*/
ret = get_hub_descriptor(hdev, hub->descriptor);
if (ret < 0) {
@@ -2079,6 +2091,12 @@ void usb_disconnect(struct usb_device **pdev)
dev_info(&udev->dev, "USB disconnect, device number %d\n",
udev->devnum);
+ /*
+ * Ensure that the pm runtime code knows that the USB device
+ * is in the process of being disconnected.
+ */
+ pm_runtime_barrier(&udev->dev);
+
usb_lock_device(udev);
hub_disconnect_children(udev);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index e5d3a0bdf32a..a2c14bb5efa4 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -23,6 +23,7 @@ void acc_disconnect(void);
static struct class *android_class;
static struct device *android_device;
static int index;
+static int gadget_index;
struct device *create_function_device(char *name)
{
@@ -1439,21 +1440,21 @@ static void android_work(struct work_struct *data)
spin_unlock_irqrestore(&cdev->lock, flags);
if (status[0]) {
- kobject_uevent_env(&android_device->kobj,
+ kobject_uevent_env(&gi->dev->kobj,
KOBJ_CHANGE, connected);
pr_info("%s: sent uevent %s\n", __func__, connected[0]);
uevent_sent = true;
}
if (status[1]) {
- kobject_uevent_env(&android_device->kobj,
+ kobject_uevent_env(&gi->dev->kobj,
KOBJ_CHANGE, configured);
pr_info("%s: sent uevent %s\n", __func__, configured[0]);
uevent_sent = true;
}
if (status[2]) {
- kobject_uevent_env(&android_device->kobj,
+ kobject_uevent_env(&gi->dev->kobj,
KOBJ_CHANGE, disconnected);
pr_info("%s: sent uevent %s\n", __func__, disconnected[0]);
uevent_sent = true;
@@ -1613,23 +1614,28 @@ static int android_device_create(struct gadget_info *gi)
{
struct device_attribute **attrs;
struct device_attribute *attr;
+ char str[10];
INIT_WORK(&gi->work, android_work);
- android_device = device_create(android_class, NULL,
- MKDEV(0, 0), NULL, "android0");
- if (IS_ERR(android_device))
- return PTR_ERR(android_device);
+ snprintf(str, sizeof(str), "android%d", gadget_index - 1);
+ pr_debug("Creating android device %s\n", str);
+ gi->dev = device_create(android_class, NULL,
+ MKDEV(0, 0), NULL, str);
+ if (IS_ERR(gi->dev))
+ return PTR_ERR(gi->dev);
- dev_set_drvdata(android_device, gi);
+ dev_set_drvdata(gi->dev, gi);
+ if (gadget_index == 1)
+ android_device = gi->dev;
attrs = android_usb_attributes;
while ((attr = *attrs++)) {
int err;
- err = device_create_file(android_device, attr);
+ err = device_create_file(gi->dev, attr);
if (err) {
- device_destroy(android_device->class,
- android_device->devt);
+ device_destroy(gi->dev->class,
+ gi->dev->devt);
return err;
}
}
@@ -1637,15 +1643,15 @@ static int android_device_create(struct gadget_info *gi)
return 0;
}
-static void android_device_destroy(void)
+static void android_device_destroy(struct device *dev)
{
struct device_attribute **attrs;
struct device_attribute *attr;
attrs = android_usb_attributes;
while ((attr = *attrs++))
- device_remove_file(android_device, attr);
- device_destroy(android_device->class, android_device->devt);
+ device_remove_file(dev, attr);
+ device_destroy(dev->class, dev->devt);
}
#else
static inline int android_device_create(struct gadget_info *gi)
@@ -1653,7 +1659,7 @@ static inline int android_device_create(struct gadget_info *gi)
return 0;
}
-static inline void android_device_destroy(void)
+static inline void android_device_destroy(struct device *dev)
{
}
#endif
@@ -1705,6 +1711,8 @@ static struct config_group *gadgets_make(
if (!gi->composite.gadget_driver.function)
goto err;
+ gadget_index++;
+ pr_debug("Creating gadget index %d\n", gadget_index);
if (android_device_create(gi) < 0)
goto err;
@@ -1719,8 +1727,14 @@ err:
static void gadgets_drop(struct config_group *group, struct config_item *item)
{
+ struct gadget_info *gi;
+
+ gi = container_of(to_config_group(item), struct gadget_info, group);
config_item_put(item);
- android_device_destroy();
+ if (gi->dev) {
+ android_device_destroy(gi->dev);
+ gi->dev = NULL;
+ }
}
static struct configfs_group_operations gadgets_ops = {
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 7c35241a487a..b6f4790ffc08 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2015,6 +2015,12 @@ static int ffs_func_eps_enable(struct ffs_function *func)
break;
}
+ /*
+ * userspace setting maxburst > 1 results more fifo
+ * allocation than without maxburst. Change maxburst to 1
+ * only to allocate fifo size of max packet size.
+ */
+ ep->ep->maxburst = 1;
ret = usb_ep_enable(ep->ep);
if (likely(!ret)) {
epfile->ep = ep;
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 999433ae2d72..d8cc5fd39e85 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -1012,6 +1012,10 @@ static void receive_file_work(struct work_struct *data)
usb_ep_dequeue(dev->ep_out, read_req);
break;
}
+ if (read_req->status) {
+ r = read_req->status;
+ break;
+ }
mutex_lock(&dev->read_mutex);
if (dev->state == STATE_OFFLINE) {
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 82483599a882..35e0c046fdcc 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1711,7 +1711,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
for (i = 0; i < num_sp; i++) {
dma_addr_t dma;
- void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma,
+ void *buf = dma_zalloc_coherent(dev, xhci->page_size, &dma,
flags);
if (!buf)
goto fail_sp5;
@@ -2768,7 +2768,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
(xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) |
xhci->cmd_ring->cycle_state;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Setting command ring address to 0x%x", val);
+ "// Setting command ring address to 0x%016llx", val_64);
xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
xhci_dbg_cmd_ptrs(xhci);
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index dd262f418140..30c4ae80c8f9 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -52,6 +52,7 @@
#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8
#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
+#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0
static const char hcd_name[] = "xhci_hcd";
@@ -167,12 +168,14 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI))
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI))
xhci->quirks |= XHCI_MISSING_CAS;
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 1ddf882fb607..56a9cd62f2c4 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -154,7 +154,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
- return -ENODEV;
+ return irq;
/* Try to set 64-bit DMA first */
if (WARN_ON(!pdev->dev.dma_mask))
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 775690bed4c0..5e43fd881a9c 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -557,7 +557,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd,
info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice);
/* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */
- info.speed = le16_to_cpu(dev->udev->speed);
+ info.speed = dev->udev->speed;
info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber;
info.report_size = dev->report_size;
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 4dd531ac5a7f..0ec9ee573ffa 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -317,9 +317,16 @@ static int tower_open (struct inode *inode, struct file *file)
int subminor;
int retval = 0;
struct usb_interface *interface;
- struct tower_reset_reply reset_reply;
+ struct tower_reset_reply *reset_reply;
int result;
+ reset_reply = kmalloc(sizeof(*reset_reply), GFP_KERNEL);
+
+ if (!reset_reply) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
nonseekable_open(inode, file);
subminor = iminor(inode);
@@ -364,8 +371,8 @@ static int tower_open (struct inode *inode, struct file *file)
USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
0,
0,
- &reset_reply,
- sizeof(reset_reply),
+ reset_reply,
+ sizeof(*reset_reply),
1000);
if (result < 0) {
dev_err(&dev->udev->dev,
@@ -406,6 +413,7 @@ unlock_exit:
mutex_unlock(&dev->lock);
exit:
+ kfree(reset_reply);
return retval;
}
@@ -808,7 +816,7 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
struct lego_usb_tower *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor* endpoint;
- struct tower_get_version_reply get_version_reply;
+ struct tower_get_version_reply *get_version_reply = NULL;
int i;
int retval = -ENOMEM;
int result;
@@ -898,6 +906,13 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
+ get_version_reply = kmalloc(sizeof(*get_version_reply), GFP_KERNEL);
+
+ if (!get_version_reply) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
/* get the firmware version and log it */
result = usb_control_msg (udev,
usb_rcvctrlpipe(udev, 0),
@@ -905,18 +920,19 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
0,
0,
- &get_version_reply,
- sizeof(get_version_reply),
+ get_version_reply,
+ sizeof(*get_version_reply),
1000);
if (result < 0) {
dev_err(idev, "LEGO USB Tower get version control request failed\n");
retval = result;
goto error;
}
- dev_info(&interface->dev, "LEGO USB Tower firmware version is %d.%d "
- "build %d\n", get_version_reply.major,
- get_version_reply.minor,
- le16_to_cpu(get_version_reply.build_no));
+ dev_info(&interface->dev,
+ "LEGO USB Tower firmware version is %d.%d build %d\n",
+ get_version_reply->major,
+ get_version_reply->minor,
+ le16_to_cpu(get_version_reply->build_no));
/* we can register the device now, as it is ready */
usb_set_intfdata (interface, dev);
@@ -937,9 +953,11 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
USB_MAJOR, dev->minor);
exit:
+ kfree(get_version_reply);
return retval;
error:
+ kfree(get_version_reply);
tower_delete(dev);
return retval;
}
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 1624b09d9748..2e947dc94e32 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -135,6 +135,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
case USB_ENDPOINT_XFER_INT:
if (dev->info->intr)
goto try_intr;
+ continue;
case USB_ENDPOINT_XFER_ISOC:
if (dev->info->iso)
goto try_iso;
diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c
index 4c82077da475..6020024cb87c 100644
--- a/drivers/usb/musb/tusb6010_omap.c
+++ b/drivers/usb/musb/tusb6010_omap.c
@@ -220,6 +220,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz,
u32 dma_remaining;
int src_burst, dst_burst;
u16 csr;
+ u32 psize;
int ch;
s8 dmareq;
s8 sync_dev;
@@ -391,15 +392,19 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz,
if (chdat->tx) {
/* Send transfer_packet_sz packets at a time */
- musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
- chdat->transfer_packet_sz);
+ psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET);
+ psize &= ~0x7ff;
+ psize |= chdat->transfer_packet_sz;
+ musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize);
musb_writel(ep_conf, TUSB_EP_TX_OFFSET,
TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
} else {
/* Receive transfer_packet_sz packets at a time */
- musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
- chdat->transfer_packet_sz << 16);
+ psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET);
+ psize &= ~(0x7ff << 16);
+ psize |= (chdat->transfer_packet_sz << 16);
+ musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize);
musb_writel(ep_conf, TUSB_EP_RX_OFFSET,
TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 7a250c31f44d..03aeec2e878c 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -34,6 +34,10 @@ static bool usb_compliance_mode;
module_param(usb_compliance_mode, bool, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(usb_compliance_mode, "Start USB stack for USB3.1 compliance testing");
+static bool disable_usb_pd;
+module_param(disable_usb_pd, bool, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(disable_usb_pd, "Disable USB PD for USB3.1 compliance testing");
+
enum usbpd_state {
PE_UNKNOWN,
PE_ERROR_RECOVERY,
@@ -920,7 +924,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
break;
}
- if (!val.intval)
+ if (!val.intval || disable_usb_pd)
break;
pd_reset_protocol(pd);
@@ -2552,6 +2556,7 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role,
{
struct usbpd *pd = dual_role_get_drvdata(dual_role);
bool do_swap = false;
+ int wait_count = 5;
if (!pd)
return -ENODEV;
@@ -2578,9 +2583,15 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role,
set_power_role(pd, PR_NONE);
/* wait until it takes effect */
- while (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE)
+ while (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE &&
+ --wait_count)
msleep(20);
+ if (!wait_count) {
+ usbpd_err(&pd->dev, "setting mode timed out\n");
+ return -ETIMEDOUT;
+ }
+
break;
case DUAL_ROLE_PROP_DR:
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index b3a21fcbbaf9..e0385d6c0abb 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -809,10 +809,10 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) },
{ USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) },
{ USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
@@ -873,6 +873,7 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID,
USB_CLASS_VENDOR_SPEC,
USB_SUBCLASS_VENDOR_SPEC, 0x00) },
+ { USB_DEVICE_INTERFACE_NUMBER(ACTEL_VID, MICROSEMI_ARROW_SF2PLUS_BOARD_PID, 2) },
{ USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
{ USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
@@ -1507,9 +1508,9 @@ static int set_serial_info(struct tty_struct *tty,
(new_serial.flags & ASYNC_FLAGS));
priv->custom_divisor = new_serial.custom_divisor;
+check_and_exit:
write_latency_timer(port);
-check_and_exit:
if ((old_priv.flags & ASYNC_SPD_MASK) !=
(priv->flags & ASYNC_SPD_MASK)) {
if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 48ee04c94a75..4fcf1cecb6d7 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -873,9 +873,17 @@
#define FIC_VID 0x1457
#define FIC_NEO1973_DEBUG_PID 0x5118
+/*
+ * Actel / Microsemi
+ */
+#define ACTEL_VID 0x1514
+#define MICROSEMI_ARROW_SF2PLUS_BOARD_PID 0x2008
+
/* Olimex */
#define OLIMEX_VID 0x15BA
#define OLIMEX_ARM_USB_OCD_PID 0x0003
+#define OLIMEX_ARM_USB_TINY_PID 0x0004
+#define OLIMEX_ARM_USB_TINY_H_PID 0x002a
#define OLIMEX_ARM_USB_OCD_H_PID 0x002b
/*
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index f1a8fdcd8674..e98532feb0cc 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2349,8 +2349,11 @@ static void change_port_settings(struct tty_struct *tty,
if (!baud) {
/* pick a default, any default... */
baud = 9600;
- } else
+ } else {
+ /* Avoid a zero divisor. */
+ baud = min(baud, 461550);
tty_encode_baud_rate(tty, baud, baud);
+ }
edge_port->baud_rate = baud;
config->wBaudRate = (__u16)((461550L + baud/2) / baud);
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 9bf82c262c5b..a6c07c6be25f 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -189,7 +189,7 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty,
return -ENOMEM;
divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
- put_unaligned_le32(cpu_to_le32(divisor), buf);
+ put_unaligned_le32(divisor, buf);
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
MCT_U232_SET_BAUD_RATE_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index af67a0de6b5d..3bf61acfc26b 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -281,6 +281,7 @@ static void option_instat_callback(struct urb *urb);
#define TELIT_PRODUCT_LE922_USBCFG0 0x1042
#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
+#define TELIT_PRODUCT_ME910 0x1100
#define TELIT_PRODUCT_LE920 0x1200
#define TELIT_PRODUCT_LE910 0x1201
#define TELIT_PRODUCT_LE910_USBCFG4 0x1206
@@ -640,6 +641,11 @@ static const struct option_blacklist_info simcom_sim7100e_blacklist = {
.reserved = BIT(5) | BIT(6),
};
+static const struct option_blacklist_info telit_me910_blacklist = {
+ .sendsetup = BIT(0),
+ .reserved = BIT(1) | BIT(3),
+};
+
static const struct option_blacklist_info telit_le910_blacklist = {
.sendsetup = BIT(0),
.reserved = BIT(1) | BIT(2),
@@ -1235,6 +1241,8 @@ static const struct usb_device_id option_ids[] = {
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
+ .driver_info = (kernel_ulong_t)&telit_me910_blacklist },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
.driver_info = (kernel_ulong_t)&telit_le910_blacklist },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 38b3f0d8cd58..fd509ed6cf70 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -162,6 +162,8 @@ static const struct usb_device_id id_table[] = {
{DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx */
{DEVICE_SWI(0x1199, 0x9078)}, /* Sierra Wireless EM74xx */
{DEVICE_SWI(0x1199, 0x9079)}, /* Sierra Wireless EM74xx */
+ {DEVICE_SWI(0x1199, 0x907a)}, /* Sierra Wireless EM74xx QDL */
+ {DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */
{DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index f3cf4cecd2b7..091e8ec7a6c0 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -446,6 +446,10 @@ struct ms_lib_ctrl {
#define SD_BLOCK_LEN 9
struct ene_ub6250_info {
+
+ /* I/O bounce buffer */
+ u8 *bbuf;
+
/* for 6250 code */
struct SD_STATUS SD_Status;
struct MS_STATUS MS_Status;
@@ -493,8 +497,11 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag);
static void ene_ub6250_info_destructor(void *extra)
{
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) extra;
+
if (!extra)
return;
+ kfree(info->bbuf);
}
static int ene_send_scsi_cmd(struct us_data *us, u8 fDir, void *buf, int use_sg)
@@ -858,8 +865,9 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr,
u8 PageNum, u32 *PageBuf, struct ms_lib_type_extdat *ExtraDat)
{
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
int result;
- u8 ExtBuf[4];
u32 bn = PhyBlockAddr * 0x20 + PageNum;
/* printk(KERN_INFO "MS --- MS_ReaderReadPage,
@@ -902,7 +910,7 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr,
bcb->CDB[2] = (unsigned char)(PhyBlockAddr>>16);
bcb->CDB[6] = 0x01;
- result = ene_send_scsi_cmd(us, FDIR_READ, &ExtBuf, 0);
+ result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
@@ -911,9 +919,9 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr,
ExtraDat->status0 = 0x10; /* Not yet,fireware support */
ExtraDat->status1 = 0x00; /* Not yet,fireware support */
- ExtraDat->ovrflg = ExtBuf[0];
- ExtraDat->mngflg = ExtBuf[1];
- ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]);
+ ExtraDat->ovrflg = bbuf[0];
+ ExtraDat->mngflg = bbuf[1];
+ ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]);
return USB_STOR_TRANSPORT_GOOD;
}
@@ -1339,8 +1347,9 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock,
u8 PageNum, struct ms_lib_type_extdat *ExtraDat)
{
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
int result;
- u8 ExtBuf[4];
/* printk("MS_LibReadExtra --- PhyBlock = %x, PageNum = %x\n", PhyBlock, PageNum); */
memset(bcb, 0, sizeof(struct bulk_cb_wrap));
@@ -1355,7 +1364,7 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock,
bcb->CDB[2] = (unsigned char)(PhyBlock>>16);
bcb->CDB[6] = 0x01;
- result = ene_send_scsi_cmd(us, FDIR_READ, &ExtBuf, 0);
+ result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
@@ -1363,9 +1372,9 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock,
ExtraDat->intr = 0x80; /* Not yet, waiting for fireware support */
ExtraDat->status0 = 0x10; /* Not yet, waiting for fireware support */
ExtraDat->status1 = 0x00; /* Not yet, waiting for fireware support */
- ExtraDat->ovrflg = ExtBuf[0];
- ExtraDat->mngflg = ExtBuf[1];
- ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]);
+ ExtraDat->ovrflg = bbuf[0];
+ ExtraDat->mngflg = bbuf[1];
+ ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]);
return USB_STOR_TRANSPORT_GOOD;
}
@@ -1569,9 +1578,9 @@ static int ms_lib_scan_logicalblocknumber(struct us_data *us, u16 btBlk1st)
u16 PhyBlock, newblk, i;
u16 LogStart, LogEnde;
struct ms_lib_type_extdat extdat;
- u8 buf[0x200];
u32 count = 0, index = 0;
struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
for (PhyBlock = 0; PhyBlock < info->MS_Lib.NumberOfPhyBlock;) {
ms_lib_phy_to_log_range(PhyBlock, &LogStart, &LogEnde);
@@ -1585,14 +1594,16 @@ static int ms_lib_scan_logicalblocknumber(struct us_data *us, u16 btBlk1st)
}
if (count == PhyBlock) {
- ms_lib_read_extrablock(us, PhyBlock, 0, 0x80, &buf);
+ ms_lib_read_extrablock(us, PhyBlock, 0, 0x80,
+ bbuf);
count += 0x80;
}
index = (PhyBlock % 0x80) * 4;
- extdat.ovrflg = buf[index];
- extdat.mngflg = buf[index+1];
- extdat.logadr = memstick_logaddr(buf[index+2], buf[index+3]);
+ extdat.ovrflg = bbuf[index];
+ extdat.mngflg = bbuf[index+1];
+ extdat.logadr = memstick_logaddr(bbuf[index+2],
+ bbuf[index+3]);
if ((extdat.ovrflg & MS_REG_OVR_BKST) != MS_REG_OVR_BKST_OK) {
ms_lib_setacquired_errorblock(us, PhyBlock);
@@ -2075,9 +2086,9 @@ static int ene_ms_init(struct us_data *us)
{
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
int result;
- u8 buf[0x200];
u16 MSP_BlockSize, MSP_UserAreaBlocks;
struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
printk(KERN_INFO "transport --- ENE_MSInit\n");
@@ -2096,13 +2107,13 @@ static int ene_ms_init(struct us_data *us)
bcb->CDB[0] = 0xF1;
bcb->CDB[1] = 0x01;
- result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0);
+ result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0);
if (result != USB_STOR_XFER_GOOD) {
printk(KERN_ERR "Execution MS Init Code Fail !!\n");
return USB_STOR_TRANSPORT_ERROR;
}
/* the same part to test ENE */
- info->MS_Status = *(struct MS_STATUS *)&buf[0];
+ info->MS_Status = *(struct MS_STATUS *) bbuf;
if (info->MS_Status.Insert && info->MS_Status.Ready) {
printk(KERN_INFO "Insert = %x\n", info->MS_Status.Insert);
@@ -2111,15 +2122,15 @@ static int ene_ms_init(struct us_data *us)
printk(KERN_INFO "IsMSPHG = %x\n", info->MS_Status.IsMSPHG);
printk(KERN_INFO "WtP= %x\n", info->MS_Status.WtP);
if (info->MS_Status.IsMSPro) {
- MSP_BlockSize = (buf[6] << 8) | buf[7];
- MSP_UserAreaBlocks = (buf[10] << 8) | buf[11];
+ MSP_BlockSize = (bbuf[6] << 8) | bbuf[7];
+ MSP_UserAreaBlocks = (bbuf[10] << 8) | bbuf[11];
info->MSP_TotalBlock = MSP_BlockSize * MSP_UserAreaBlocks;
} else {
ms_card_init(us); /* Card is MS (to ms.c)*/
}
usb_stor_dbg(us, "MS Init Code OK !!\n");
} else {
- usb_stor_dbg(us, "MS Card Not Ready --- %x\n", buf[0]);
+ usb_stor_dbg(us, "MS Card Not Ready --- %x\n", bbuf[0]);
return USB_STOR_TRANSPORT_ERROR;
}
@@ -2129,9 +2140,9 @@ static int ene_ms_init(struct us_data *us)
static int ene_sd_init(struct us_data *us)
{
int result;
- u8 buf[0x200];
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
usb_stor_dbg(us, "transport --- ENE_SDInit\n");
/* SD Init Part-1 */
@@ -2165,17 +2176,17 @@ static int ene_sd_init(struct us_data *us)
bcb->Flags = US_BULK_FLAG_IN;
bcb->CDB[0] = 0xF1;
- result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0);
+ result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0);
if (result != USB_STOR_XFER_GOOD) {
usb_stor_dbg(us, "Execution SD Init Code Fail !!\n");
return USB_STOR_TRANSPORT_ERROR;
}
- info->SD_Status = *(struct SD_STATUS *)&buf[0];
+ info->SD_Status = *(struct SD_STATUS *) bbuf;
if (info->SD_Status.Insert && info->SD_Status.Ready) {
struct SD_STATUS *s = &info->SD_Status;
- ene_get_card_status(us, (unsigned char *)&buf);
+ ene_get_card_status(us, bbuf);
usb_stor_dbg(us, "Insert = %x\n", s->Insert);
usb_stor_dbg(us, "Ready = %x\n", s->Ready);
usb_stor_dbg(us, "IsMMC = %x\n", s->IsMMC);
@@ -2183,7 +2194,7 @@ static int ene_sd_init(struct us_data *us)
usb_stor_dbg(us, "HiSpeed = %x\n", s->HiSpeed);
usb_stor_dbg(us, "WtP = %x\n", s->WtP);
} else {
- usb_stor_dbg(us, "SD Card Not Ready --- %x\n", buf[0]);
+ usb_stor_dbg(us, "SD Card Not Ready --- %x\n", bbuf[0]);
return USB_STOR_TRANSPORT_ERROR;
}
return USB_STOR_TRANSPORT_GOOD;
@@ -2193,13 +2204,15 @@ static int ene_sd_init(struct us_data *us)
static int ene_init(struct us_data *us)
{
int result;
- u8 misc_reg03 = 0;
+ u8 misc_reg03;
struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra);
+ u8 *bbuf = info->bbuf;
- result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03);
+ result = ene_get_card_type(us, REG_CARD_STATUS, bbuf);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
+ misc_reg03 = bbuf[0];
if (misc_reg03 & 0x01) {
if (!info->SD_Status.Ready) {
result = ene_sd_init(us);
@@ -2316,8 +2329,9 @@ static int ene_ub6250_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int result;
- u8 misc_reg03 = 0;
+ u8 misc_reg03;
struct us_data *us;
+ struct ene_ub6250_info *info;
result = usb_stor_probe1(&us, intf, id,
(id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list,
@@ -2326,11 +2340,16 @@ static int ene_ub6250_probe(struct usb_interface *intf,
return result;
/* FIXME: where should the code alloc extra buf ? */
- if (!us->extra) {
- us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL);
- if (!us->extra)
- return -ENOMEM;
- us->extra_destructor = ene_ub6250_info_destructor;
+ us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL);
+ if (!us->extra)
+ return -ENOMEM;
+ us->extra_destructor = ene_ub6250_info_destructor;
+
+ info = (struct ene_ub6250_info *)(us->extra);
+ info->bbuf = kmalloc(512, GFP_KERNEL);
+ if (!info->bbuf) {
+ kfree(us->extra);
+ return -ENOMEM;
}
us->transport_name = "ene_ub6250";
@@ -2342,12 +2361,13 @@ static int ene_ub6250_probe(struct usb_interface *intf,
return result;
/* probe card type */
- result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03);
+ result = ene_get_card_type(us, REG_CARD_STATUS, info->bbuf);
if (result != USB_STOR_XFER_GOOD) {
usb_stor_disconnect(intf);
return USB_STOR_TRANSPORT_ERROR;
}
+ misc_reg03 = info->bbuf[0];
if (!(misc_reg03 & 0x01)) {
pr_info("ums_eneub6250: This driver only supports SD/MS cards. "
"It does not support SM cards.\n");
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c
index 6345e85822a4..a50cf45e530f 100644
--- a/drivers/uwb/i1480/dfu/usb.c
+++ b/drivers/uwb/i1480/dfu/usb.c
@@ -341,6 +341,7 @@ error_submit_ep1:
static
int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
+ struct usb_device *udev = interface_to_usbdev(iface);
struct i1480_usb *i1480_usb;
struct i1480 *i1480;
struct device *dev = &iface->dev;
@@ -352,8 +353,8 @@ int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
iface->cur_altsetting->desc.bInterfaceNumber);
goto error;
}
- if (iface->num_altsetting > 1
- && interface_to_usbdev(iface)->descriptor.idProduct == 0xbabe) {
+ if (iface->num_altsetting > 1 &&
+ le16_to_cpu(udev->descriptor.idProduct) == 0xbabe) {
/* Need altsetting #1 [HW QUIRK] or EP1 won't work */
result = usb_set_interface(interface_to_usbdev(iface), 0, 1);
if (result < 0)
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index ecb826eefe02..2fa280671c1e 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -130,57 +130,34 @@ static void vfio_unlink_dma(struct vfio_iommu *iommu, struct vfio_dma *old)
rb_erase(&old->node, &iommu->dma_list);
}
-struct vwork {
- struct mm_struct *mm;
- long npage;
- struct work_struct work;
-};
-
-/* delayed decrement/increment for locked_vm */
-static void vfio_lock_acct_bg(struct work_struct *work)
+static int vfio_lock_acct(long npage, bool *lock_cap)
{
- struct vwork *vwork = container_of(work, struct vwork, work);
- struct mm_struct *mm;
-
- mm = vwork->mm;
- down_write(&mm->mmap_sem);
- mm->locked_vm += vwork->npage;
- up_write(&mm->mmap_sem);
- mmput(mm);
- kfree(vwork);
-}
+ int ret = 0;
-static void vfio_lock_acct(long npage)
-{
- struct vwork *vwork;
- struct mm_struct *mm;
+ if (!npage)
+ return 0;
- if (!current->mm || !npage)
- return; /* process exited or nothing to do */
+ if (!current->mm)
+ return -ESRCH; /* process exited */
- if (down_write_trylock(&current->mm->mmap_sem)) {
- current->mm->locked_vm += npage;
- up_write(&current->mm->mmap_sem);
- return;
- }
+ down_write(&current->mm->mmap_sem);
+ if (npage > 0) {
+ if (lock_cap ? !*lock_cap : !capable(CAP_IPC_LOCK)) {
+ unsigned long limit;
- /*
- * Couldn't get mmap_sem lock, so must setup to update
- * mm->locked_vm later. If locked_vm were atomic, we
- * wouldn't need this silliness
- */
- vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL);
- if (!vwork)
- return;
- mm = get_task_mm(current);
- if (!mm) {
- kfree(vwork);
- return;
+ limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+
+ if (current->mm->locked_vm + npage > limit)
+ ret = -ENOMEM;
+ }
}
- INIT_WORK(&vwork->work, vfio_lock_acct_bg);
- vwork->mm = mm;
- vwork->npage = npage;
- schedule_work(&vwork->work);
+
+ if (!ret)
+ current->mm->locked_vm += npage;
+
+ up_write(&current->mm->mmap_sem);
+
+ return ret;
}
/*
@@ -262,9 +239,9 @@ static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn)
static long vfio_pin_pages(unsigned long vaddr, long npage,
int prot, unsigned long *pfn_base)
{
- unsigned long limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ unsigned long pfn = 0, limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
bool lock_cap = capable(CAP_IPC_LOCK);
- long ret, i;
+ long ret, i = 1;
bool rsvd;
if (!current->mm)
@@ -283,16 +260,11 @@ static long vfio_pin_pages(unsigned long vaddr, long npage,
return -ENOMEM;
}
- if (unlikely(disable_hugepages)) {
- if (!rsvd)
- vfio_lock_acct(1);
- return 1;
- }
+ if (unlikely(disable_hugepages))
+ goto out;
/* Lock all the consecutive pages from pfn_base */
- for (i = 1, vaddr += PAGE_SIZE; i < npage; i++, vaddr += PAGE_SIZE) {
- unsigned long pfn = 0;
-
+ for (vaddr += PAGE_SIZE; i < npage; i++, vaddr += PAGE_SIZE) {
ret = vaddr_get_pfn(vaddr, prot, &pfn);
if (ret)
break;
@@ -308,12 +280,24 @@ static long vfio_pin_pages(unsigned long vaddr, long npage,
put_pfn(pfn, prot);
pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n",
__func__, limit << PAGE_SHIFT);
- break;
+ ret = -ENOMEM;
+ goto unpin_out;
}
}
+out:
if (!rsvd)
- vfio_lock_acct(i);
+ ret = vfio_lock_acct(i, &lock_cap);
+
+unpin_out:
+ if (ret) {
+ if (!rsvd) {
+ for (pfn = *pfn_base ; i ; pfn++, i--)
+ put_pfn(pfn, prot);
+ }
+
+ return ret;
+ }
return i;
}
@@ -328,7 +312,7 @@ static long vfio_unpin_pages(unsigned long pfn, long npage,
unlocked += put_pfn(pfn++, prot);
if (do_accounting)
- vfio_lock_acct(-unlocked);
+ vfio_lock_acct(-unlocked, NULL);
return unlocked;
}
@@ -390,7 +374,7 @@ static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma)
cond_resched();
}
- vfio_lock_acct(-unlocked);
+ vfio_lock_acct(-unlocked, NULL);
}
static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 7d32e2c5cc0d..7ab2fb13061d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -25,6 +25,7 @@ source "drivers/gpu/msm/Kconfig"
source "drivers/gpu/drm/Kconfig"
+source "drivers/video/msm/ba/Kconfig"
menu "Frame buffer Devices"
source "drivers/video/fbdev/Kconfig"
endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 1a8c4ced39b2..0a190665a4cf 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MSM_BA_V4L2) += msm/ba/
obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 1c984d02755e..d6e8a213c215 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -272,7 +272,7 @@ struct mdss_smmu_ops {
void (*smmu_unmap_dma_buf)(struct sg_table *table, int domain,
int dir, struct dma_buf *dma_buf);
int (*smmu_dma_alloc_coherent)(struct device *dev, size_t size,
- dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr,
+ dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr,
gfp_t gfp, int domain);
void (*smmu_dma_free_coherent)(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t phys, dma_addr_t iova,
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 2b9c71441d68..2f5aad8ed801 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -3493,6 +3493,7 @@ static int __copy_layer_pp_info_igc_params(
compat_ptr(pp_info32->igc_cfg.c0_c1_data);
pp_info->igc_cfg.c2_data =
compat_ptr(pp_info32->igc_cfg.c2_data);
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3565,6 +3566,7 @@ static int __copy_layer_pp_info_hist_lut_params(
pp_info->hist_lut_cfg.len = pp_info32->hist_lut_cfg.len;
pp_info->hist_lut_cfg.data =
compat_ptr(pp_info32->hist_lut_cfg.data);
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3654,6 +3656,7 @@ static int __copy_layer_pp_info_pa_v2_params(
break;
default:
pr_debug("version invalid\n");
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3737,6 +3740,7 @@ static int __copy_layer_pp_info_pcc_params(
break;
default:
pr_debug("version invalid, fallback to legacy\n");
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 8cb6c7157230..230b02061b39 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -454,6 +454,9 @@ static ssize_t mdss_debug_base_offset_write(struct file *file,
sscanf(buf, "%5x %x", &off, &cnt);
+ if (off % sizeof(u32))
+ return -EINVAL;
+
if (off > dbg->max_offset)
return -EINVAL;
@@ -526,6 +529,9 @@ static ssize_t mdss_debug_base_reg_write(struct file *file,
if (cnt < 2)
return -EFAULT;
+ if (off % sizeof(u32))
+ return -EFAULT;
+
if (off >= dbg->max_offset)
return -EFAULT;
@@ -571,6 +577,9 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
return -ENOMEM;
}
+ if (dbg->off % sizeof(u32))
+ return -EFAULT;
+
ptr = dbg->base + dbg->off;
tot = 0;
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index a74bf6f60774..7d4578b08244 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -1455,7 +1455,7 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp,
mdss_dp_config_misc(dp,
mdss_dp_bpp_to_test_bit_depth(mdss_dp_get_bpp(dp)),
mdss_dp_get_colorimetry_config(dp));
- mdss_dp_sw_config_msa(&dp->ctrl_io, dp->link_rate, &dp->dp_cc_io);
+ mdss_dp_sw_config_msa(dp);
mdss_dp_timing_cfg(&dp->ctrl_io, &dp->panel_data.panel_info);
}
@@ -1715,7 +1715,7 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp)
{
- return dp->dpcd.downstream_port.dfp_present;
+ return dp->dpcd.downstream_port.dsp_present;
}
static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp)
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index f358aad8a667..afa8e3db590f 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -230,14 +230,38 @@ struct dp_alt_mode {
#define DP_LINK_RATE_MULTIPLIER 27000000
#define DP_KHZ_TO_HZ 1000
#define DP_MAX_PIXEL_CLK_KHZ 675000
+
+enum downstream_port_type {
+ DSP_TYPE_DP = 0x00,
+ DSP_TYPE_VGA,
+ DSP_TYPE_DVI_HDMI_DPPP,
+ DSP_TYPE_OTHER,
+};
+
+static inline char *mdss_dp_dsp_type_to_string(u32 dsp_type)
+{
+ switch (dsp_type) {
+ case DSP_TYPE_DP:
+ return DP_ENUM_STR(DSP_TYPE_DP);
+ case DSP_TYPE_VGA:
+ return DP_ENUM_STR(DSP_TYPE_VGA);
+ case DSP_TYPE_DVI_HDMI_DPPP:
+ return DP_ENUM_STR(DSP_TYPE_DVI_HDMI_DPPP);
+ case DSP_TYPE_OTHER:
+ return DP_ENUM_STR(DSP_TYPE_OTHER);
+ default:
+ return "unknown";
+ }
+}
+
struct downstream_port_config {
/* Byte 02205h */
- bool dfp_present;
- u32 dfp_type;
+ bool dsp_present;
+ enum downstream_port_type dsp_type;
bool format_conversion;
bool detailed_cap_info_available;
/* Byte 02207h */
- u32 dfp_count;
+ u32 dsp_count;
bool msa_timing_par_ignored;
bool oui_support;
};
@@ -1139,6 +1163,12 @@ static inline void mdss_dp_reset_frame_crc_data(struct mdss_dp_crc_data *crc)
crc->en = false;
}
+static inline bool mdss_dp_is_dsp_type_vga(struct mdss_dp_drv_pdata *dp)
+{
+ return (dp->dpcd.downstream_port.dsp_present &&
+ (dp->dpcd.downstream_port.dsp_type == DSP_TYPE_VGA));
+}
+
void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp);
int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp);
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 37209c161366..23a63121b78c 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -633,7 +633,8 @@ void dp_extract_edid_video_support(struct edp_edid *edid, char *buf)
pr_debug("Digital Video intf=%d color_depth=%d\n",
edid->video_intf, edid->color_depth);
} else {
- pr_err("Error, Analog video interface\n");
+ pr_debug("Analog video interface, set color depth to 8\n");
+ edid->color_depth = DP_TEST_BIT_DEPTH_8;
}
};
@@ -1140,13 +1141,13 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
pr_debug("rx_ports=%d", cap->num_rx_port);
data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */
- cap->downstream_port.dfp_present = data & BIT(0);
- cap->downstream_port.dfp_type = data & 0x6;
+ cap->downstream_port.dsp_present = data & BIT(0);
+ cap->downstream_port.dsp_type = (data & 0x6) >> 1;
cap->downstream_port.format_conversion = data & BIT(3);
cap->downstream_port.detailed_cap_info_available = data & BIT(4);
- pr_debug("dfp_present = %d, dfp_type = %d\n",
- cap->downstream_port.dfp_present,
- cap->downstream_port.dfp_type);
+ pr_debug("dsp_present = %d, dsp_type = %d\n",
+ cap->downstream_port.dsp_present,
+ cap->downstream_port.dsp_type);
pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n",
cap->downstream_port.format_conversion,
cap->downstream_port.detailed_cap_info_available);
@@ -1154,16 +1155,16 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
bp += 1; /* Skip Byte 6 */
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) {
+ cap->downstream_port.dsp_count = data & 0x7;
+ if (cap->downstream_port.dsp_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.dsp_count, DP_MAX_DS_PORT_COUNT);
+ cap->downstream_port.dsp_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",
- cap->downstream_port.dfp_count,
+ pr_debug("dsp_count = %d, msa_timing_par_ignored = %d\n",
+ cap->downstream_port.dsp_count,
cap->downstream_port.msa_timing_par_ignored);
pr_debug("oui_support = %d\n", cap->downstream_port.oui_support);
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index e8c3fe9f4957..f7b3d4664e86 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -786,19 +786,56 @@ void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io,
writel_relaxed(data, ctrl_io->base + DP_ACTIVE_HOR_VER);
}
-void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io,
- char lrate, struct dss_io_data *dp_cc_io)
+static bool use_fixed_nvid(struct mdss_dp_drv_pdata *dp)
+{
+ /*
+ * For better interop experience, used a fixed NVID=0x8000
+ * whenever connected to a VGA dongle downstream
+ */
+ return mdss_dp_is_dsp_type_vga(dp);
+}
+
+void mdss_dp_sw_config_msa(struct mdss_dp_drv_pdata *dp)
{
u32 pixel_m, pixel_n;
u32 mvid, nvid;
-
- pixel_m = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_M);
- pixel_n = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_N);
- pr_debug("pixel_m=0x%x, pixel_n=0x%x\n",
- pixel_m, pixel_n);
-
- mvid = (pixel_m & 0xFFFF) * 5;
- nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+ u64 mvid_calc;
+ u32 const nvid_fixed = 0x8000;
+ struct dss_io_data *ctrl_io = &dp->ctrl_io;
+ struct dss_io_data *dp_cc_io = &dp->dp_cc_io;
+ u32 lrate_kbps;
+ u64 stream_rate_khz;
+
+ if (use_fixed_nvid(dp)) {
+ pr_debug("use fixed NVID=0x%x\n", nvid_fixed);
+ nvid = nvid_fixed;
+
+ lrate_kbps = dp->link_rate * DP_LINK_RATE_MULTIPLIER /
+ DP_KHZ_TO_HZ;
+ stream_rate_khz = div_u64(dp->panel_data.panel_info.clk_rate,
+ DP_KHZ_TO_HZ);
+ pr_debug("link rate=%dkbps, stream_rate_khz=%lluKhz",
+ lrate_kbps, stream_rate_khz);
+
+ /*
+ * For intermediate results, use 64 bit arithmetic to avoid
+ * loss of precision.
+ */
+ mvid_calc = stream_rate_khz * nvid;
+ mvid_calc = div_u64(mvid_calc, lrate_kbps);
+
+ /*
+ * truncate back to 32 bits as this final divided value will
+ * always be within the range of a 32 bit unsigned int.
+ */
+ mvid = (u32) mvid_calc;
+ } else {
+ pixel_m = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_M);
+ pixel_n = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_N);
+ pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
+ mvid = (pixel_m & 0xFFFF) * 5;
+ nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+ }
pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
writel_relaxed(mvid, ctrl_io->base + DP_SOFTWARE_MVID);
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index 4c93e48e97dc..4970f5bc3a47 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -316,8 +316,7 @@ void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data);
int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv);
-void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io,
- char lrate, struct dss_io_data *dp_cc_io);
+void mdss_dp_sw_config_msa(struct mdss_dp_drv_pdata *dp);
void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap);
void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status);
u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 4f1333426113..82f6d4a123b5 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -773,6 +773,11 @@ static ssize_t mdss_dsi_cmd_state_write(struct file *file,
int *link_state = file->private_data;
char *input;
+ if (!count) {
+ pr_err("%s: Zero bytes to be written\n", __func__);
+ return -EINVAL;
+ }
+
input = kmalloc(count, GFP_KERNEL);
if (!input) {
pr_err("%s: Failed to allocate memory\n", __func__);
@@ -3429,9 +3434,10 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
hw_vsync_handler, IRQF_TRIGGER_FALLING,
"VSYNC_GPIO", ctrl_pdata);
if (rc) {
- pr_err("TE request_irq failed.\n");
+ pr_err("%s: TE request_irq failed for ESD\n", __func__);
goto error_shadow_clk_deinit;
}
+ te_irq_registered = 1;
disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
}
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 7fabd4944cbd..9847016fed29 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -459,6 +459,7 @@ struct mdss_dsi_ctrl_pdata {
bool avdd_en_gpio_invert;
int lcd_mode_sel_gpio;
int bklt_ctrl; /* backlight ctrl */
+ enum dsi_ctrl_op_mode bklt_dcs_op_mode; /* backlight dcs ctrl mode */
bool pwm_pmi;
int pwm_period;
int pwm_pmic_gpio;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index b448153df1df..c766ff983045 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -2275,6 +2275,7 @@ static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl,
bool ack_error = false;
char reg[16] = {0x0};
int repeated_bytes = 0;
+ struct mdss_dsi_ctrl_pdata *mctrl = mdss_dsi_get_other_ctrl(ctrl);
lp = (u32 *)rp->data;
temp = (u32 *)reg;
@@ -2335,7 +2336,11 @@ static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl,
off += ((cnt - 1) * 4);
for (i = 0; i < cnt; i++) {
- data = (u32)MIPI_INP((ctrl->ctrl_base) + off);
+ if (mdss_dsi_sync_wait_trigger(ctrl))
+ data = (u32)MIPI_INP((mctrl->ctrl_base) + off);
+ else
+ data = (u32)MIPI_INP((ctrl->ctrl_base) + off);
+
/* to network byte order */
if (!repeated_bytes)
*lp++ = ntohl(data);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 695dbfa95e29..dbd58f93e907 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -238,6 +238,11 @@ static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level)
cmdreq.rlen = 0;
cmdreq.cb = NULL;
+ if (ctrl->bklt_dcs_op_mode == DSI_HS_MODE)
+ cmdreq.flags |= CMD_REQ_HS_MODE;
+ else
+ cmdreq.flags |= CMD_REQ_LP_MODE;
+
mdss_dsi_cmdlist_put(ctrl, &cmdreq);
}
@@ -2413,6 +2418,13 @@ int mdss_panel_parse_bl_settings(struct device_node *np,
}
} else if (!strcmp(data, "bl_ctrl_dcs")) {
ctrl_pdata->bklt_ctrl = BL_DCS_CMD;
+ data = of_get_property(np,
+ "qcom,mdss-dsi-bl-dcs-command-state", NULL);
+ if (data && !strcmp(data, "dsi_hs_mode"))
+ ctrl_pdata->bklt_dcs_op_mode = DSI_HS_MODE;
+ else
+ ctrl_pdata->bklt_dcs_op_mode = DSI_LP_MODE;
+
pr_debug("%s: Configured DCS_CMD bklt ctrl\n",
__func__);
}
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index 518c24810acd..6e52390c2886 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -243,8 +243,8 @@ struct msm_mdp_interface {
do_div(out, 2 * max_bright);\
} while (0)
#define MDSS_BL_TO_BRIGHT(out, v, bl_max, max_bright) do {\
- out = ((v) * (max_bright));\
- do_div(out, bl_max);\
+ out = (2 * ((v) * (max_bright)) + (bl_max));\
+ do_div(out, 2 * bl_max);\
} while (0)
struct mdss_fb_file_info {
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index ddc5edbe010d..a953dd1a2ac2 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -469,8 +469,10 @@ static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int rc, page_id;
+ u32 i = 0, j, page;
ssize_t ret = strnlen(buf, PAGE_SIZE);
struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev);
+ struct msm_hdmi_mode_timing_info info = {0};
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -483,7 +485,22 @@ static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev,
return rc;
}
- edid_ctrl->page_id = page_id;
+ if (page_id > MSM_HDMI_INIT_RES_PAGE) {
+ page = MSM_HDMI_INIT_RES_PAGE;
+ while (page < page_id) {
+ j = 1;
+ while (sizeof(info) * j < PAGE_SIZE) {
+ i++;
+ j++;
+ }
+ page++;
+ }
+ }
+
+ if (i < HDMI_VFRMT_MAX)
+ edid_ctrl->page_id = page_id;
+ else
+ DEV_ERR("%s: invalid page id\n", __func__);
DEV_DBG("%s: %d\n", __func__, edid_ctrl->page_id);
return ret;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index a9ab970fb4bc..2e267f2695d7 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -581,7 +581,8 @@ static ssize_t hdmi_tx_sysfs_wta_edid(struct device *dev,
}
mutex_lock(&hdmi_ctrl->tx_lock);
- if (edid_size < EDID_BLOCK_SIZE) {
+ if ((edid_size < EDID_BLOCK_SIZE) ||
+ (edid_size > hdmi_ctrl->edid_buf_size)) {
DEV_DBG("%s: disabling custom edid\n", __func__);
ret = -EINVAL;
@@ -633,6 +634,11 @@ static ssize_t hdmi_tx_sysfs_rda_edid(struct device *dev,
mutex_lock(&hdmi_ctrl->tx_lock);
cea_blks = hdmi_ctrl->edid_buf[EDID_BLOCK_SIZE - 2];
+ if (cea_blks >= MAX_EDID_BLOCKS) {
+ DEV_ERR("%s: invalid cea blocks\n", __func__);
+ mutex_unlock(&hdmi_ctrl->tx_lock);
+ return -EINVAL;
+ }
size = (cea_blks + 1) * EDID_BLOCK_SIZE;
size = min_t(u32, size, PAGE_SIZE);
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 5a15b557e5c7..feea8986af91 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -586,6 +586,7 @@ struct mdss_mdp_ctl {
struct mdss_mdp_avr_info avr_info;
bool commit_in_progress;
struct mutex ds_lock;
+ bool need_vsync_on;
};
struct mdss_mdp_mixer {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 0da462394ab8..efd681a5d954 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -2513,6 +2513,7 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc(
u32 nmixers_wb;
u32 i;
u32 nmixers;
+ u32 nmixers_active;
struct mdss_mdp_mixer *mixer_pool = NULL;
if (!ctl || !ctl->mdata)
@@ -2526,10 +2527,21 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc(
case MDSS_MDP_MIXER_TYPE_INTF:
mixer_pool = ctl->mdata->mixer_intf;
nmixers = nmixers_intf;
+ nmixers_active = nmixers;
+
+ for (i = 0; i < nmixers; i++) {
+ mixer = mixer_pool + i;
+ if (mixer->ref_cnt)
+ nmixers_active--;
+ }
+ mixer = NULL;
/*
* try to reserve first layer mixer for write back if
- * assertive display needs to be supported through wfd
+ * assertive display needs to be supported through wfd.
+ * For external displays(pluggable) and writeback avoid
+ * allocating mixers LM0 and LM1 which are allocated
+ * to primary display first.
*/
if (ctl->mdata->has_wb_ad && ctl->intf_num &&
((ctl->panel_data->panel_info.type != MIPI_CMD_PANEL) ||
@@ -2541,6 +2553,10 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc(
&& (ctl->mdata->ndspp < nmixers)) {
mixer_pool += ctl->mdata->ndspp;
nmixers -= ctl->mdata->ndspp;
+ } else if ((ctl->panel_data->panel_info.is_pluggable) &&
+ nmixers_active) {
+ mixer_pool += ctl->mdata->ndspp;
+ nmixers -= ctl->mdata->ndspp;
}
break;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index 2e017fe5ec02..747b4e3e2f81 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -3859,12 +3859,24 @@ static int mdss_mdp_cmd_reconfigure(struct mdss_mdp_ctl *ctl,
}
ctl->switch_with_handoff = false;
}
+ /*
+ * keep track of vsync, so it can be enabled as part
+ * of the post switch sequence
+ */
+ if (ctl->vsync_handler.enabled)
+ ctl->need_vsync_on = true;
mdss_mdp_ctl_stop(ctl, MDSS_PANEL_POWER_OFF);
mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_DSI_DYNAMIC_SWITCH,
(void *) mode, CTL_INTF_EVENT_FLAG_DEFAULT);
} else {
+ if (ctl->need_vsync_on &&
+ ctl->ops.add_vsync_handler) {
+ ctl->ops.add_vsync_handler(ctl,
+ &ctl->vsync_handler);
+ ctl->need_vsync_on = false;
+ }
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
}
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index a1d79a89b463..b048f356c965 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -741,14 +741,15 @@ static int __cursor_layer_check(struct msm_fb_data_type *mfd,
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
if ((layer->z_order != HW_CURSOR_STAGE(mdata))
+ || layer->flags & MDP_LAYER_FLIP_LR
|| layer->src_rect.w > mdata->max_cursor_size
|| layer->src_rect.h > mdata->max_cursor_size
|| layer->src_rect.w != layer->dst_rect.w
|| layer->src_rect.h != layer->dst_rect.h
|| !mdata->ncursor_pipes) {
- pr_err("Incorrect cursor configs for pipe:%d, cursor_pipes:%d, z_order:%d\n",
+ pr_err("Incorrect cursor configs for pipe:0x%x, ncursor_pipes:%d, z_order:%d, flags:0x%x\n",
layer->pipe_ndx, mdata->ncursor_pipes,
- layer->z_order);
+ layer->z_order, layer->flags);
pr_err("src:{%d,%d,%d,%d}, dst:{%d,%d,%d,%d}\n",
layer->src_rect.x, layer->src_rect.y,
layer->src_rect.w, layer->src_rect.h,
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 3c679877705d..91816611d24f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -4413,7 +4413,7 @@ static int mdss_mdp_hw_cursor_pipe_update(struct msm_fb_data_type *mfd,
if (!mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) {
ret = mdss_smmu_dma_alloc_coherent(&pdev->dev,
cursor_frame_size, (dma_addr_t *) &mfd->cursor_buf_phys,
- &mfd->cursor_buf_iova, mfd->cursor_buf,
+ &mfd->cursor_buf_iova, &mfd->cursor_buf,
GFP_KERNEL, MDSS_IOMMU_DOMAIN_UNSECURE);
if (ret) {
pr_err("can't allocate cursor buffer rc:%d\n", ret);
@@ -4601,7 +4601,7 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
if (!mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) {
ret = mdss_smmu_dma_alloc_coherent(&pdev->dev,
cursor_frame_size, (dma_addr_t *) &mfd->cursor_buf_phys,
- &mfd->cursor_buf_iova, mfd->cursor_buf,
+ &mfd->cursor_buf_iova, &mfd->cursor_buf,
GFP_KERNEL, MDSS_IOMMU_DOMAIN_UNSECURE);
if (ret) {
pr_err("can't allocate cursor buffer rc:%d\n", ret);
@@ -4649,7 +4649,7 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
- if (cursor->set & FB_CUR_SETIMAGE) {
+ if (mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) {
u32 cursor_addr;
ret = copy_from_user(mfd->cursor_buf, img->data,
img->width * img->height * 4);
@@ -4987,9 +4987,10 @@ static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd,
ret = mdss_fb_get_hw_caps(mfd, &metadata->data.caps);
break;
case metadata_op_get_ion_fd:
- if (mfd->fb_ion_handle) {
+ if (mfd->fb_ion_handle && mfd->fb_ion_client) {
metadata->data.fbmem_ionfd =
- dma_buf_fd(mfd->fbmem_buf, 0);
+ ion_share_dma_buf_fd(mfd->fb_ion_client,
+ mfd->fb_ion_handle);
if (metadata->data.fbmem_ionfd < 0)
pr_err("fd allocation failed. fd = %d\n",
metadata->data.fbmem_ionfd);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.h b/drivers/video/fbdev/msm/mdss_mdp_pp.h
index 809c389e99e8..136e2d79787c 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.h
@@ -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
@@ -181,7 +181,7 @@ struct mdss_pp_res_type {
struct mdp_hist_lut_data enhist_disp_cfg[MDSS_BLOCK_DISP_NUM];
struct mdp_dither_cfg_data dither_disp_cfg[MDSS_BLOCK_DISP_NUM];
struct mdp_gamut_cfg_data gamut_disp_cfg[MDSS_BLOCK_DISP_NUM];
- uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE];
+ uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE * 3];
u32 hist_data[MDSS_BLOCK_DISP_NUM][HIST_V_SIZE];
struct pp_sts_type pp_disp_sts[MDSS_BLOCK_DISP_NUM];
struct mdp_dither_cfg_data pa_dither_cfg[MDSS_BLOCK_DISP_NUM];
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 1b4765837c61..75f502415589 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -477,7 +477,7 @@ static void mdss_smmu_unmap_dma_buf_v2(struct sg_table *table, int domain,
* bank device
*/
static int mdss_smmu_dma_alloc_coherent_v2(struct device *dev, size_t size,
- dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr,
+ dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr,
gfp_t gfp, int domain)
{
struct mdss_smmu_client *mdss_smmu = mdss_smmu_get_cb(domain);
@@ -486,8 +486,8 @@ static int mdss_smmu_dma_alloc_coherent_v2(struct device *dev, size_t size,
return -EINVAL;
}
- cpu_addr = dma_alloc_coherent(mdss_smmu->base.dev, size, iova, gfp);
- if (!cpu_addr) {
+ *cpu_addr = dma_alloc_coherent(mdss_smmu->base.dev, size, iova, gfp);
+ if (!*cpu_addr) {
pr_err("dma alloc coherent failed!\n");
return -ENOMEM;
}
diff --git a/drivers/video/fbdev/msm/mdss_smmu.h b/drivers/video/fbdev/msm/mdss_smmu.h
index b1ee17a01c3f..a5c7af74cdbf 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.h
+++ b/drivers/video/fbdev/msm/mdss_smmu.h
@@ -253,7 +253,7 @@ static inline void mdss_smmu_unmap_dma_buf(struct sg_table *table, int domain,
}
static inline int mdss_smmu_dma_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr,
+ dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr,
gfp_t gfp, int domain)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
diff --git a/drivers/video/fbdev/msm/msm_dba/adv7533.c b/drivers/video/fbdev/msm/msm_dba/adv7533.c
index 63e178d76403..09632b49d33b 100644
--- a/drivers/video/fbdev/msm/msm_dba/adv7533.c
+++ b/drivers/video/fbdev/msm/msm_dba/adv7533.c
@@ -517,13 +517,25 @@ static void adv7533_parse_vreg_dt(struct device *dev,
}
mp->vreg_config[i].disable_load = val_array[i];
- pr_debug("%s: %s min=%d, max=%d, enable=%d disable=%d\n",
+ /* post-on-sleep */
+ memset(val_array, 0, sizeof(u32) * dt_vreg_total);
+ rc = of_property_read_u32_array(of_node,
+ "qcom,post-on-sleep", val_array,
+ dt_vreg_total);
+ if (rc)
+ pr_warn("%s: error read post on sleep. rc=%d\n",
+ __func__, rc);
+ else
+ mp->vreg_config[i].post_on_sleep = val_array[i];
+
+ pr_debug("%s: %s min=%d, max=%d, enable=%d disable=%d post-on-sleep=%d\n",
__func__,
mp->vreg_config[i].vreg_name,
mp->vreg_config[i].min_voltage,
mp->vreg_config[i].max_voltage,
mp->vreg_config[i].enable_load,
- mp->vreg_config[i].disable_load);
+ mp->vreg_config[i].disable_load,
+ mp->vreg_config[i].post_on_sleep);
}
devm_kfree(dev, val_array);
diff --git a/drivers/video/msm/ba/Kconfig b/drivers/video/msm/ba/Kconfig
new file mode 100644
index 000000000000..1ebc7eceeda6
--- /dev/null
+++ b/drivers/video/msm/ba/Kconfig
@@ -0,0 +1,12 @@
+#
+# MSM BA V4L2
+#
+
+config MSM_BA_V4L2
+ tristate "Qualcomm technologies Inc MSM V4L2 based BA driver"
+ depends on VIDEO_V4L2
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Enables support for the MSM V4L2 bridge abstraction
diff --git a/drivers/video/msm/ba/Makefile b/drivers/video/msm/ba/Makefile
new file mode 100644
index 000000000000..b4e7ddf3c79a
--- /dev/null
+++ b/drivers/video/msm/ba/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MSM_BA_V4L2) += msm_v4l2_ba.o \
+ msm_ba_common.o \
+ msm_ba.o \
+ msm_ba_debug.o
+
diff --git a/drivers/video/msm/ba/msm_ba.c b/drivers/video/msm/ba/msm_ba.c
new file mode 100644
index 000000000000..3e0838115ca6
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba.c
@@ -0,0 +1,892 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/msm_ba.h>
+
+#include "msm_ba_internal.h"
+#include "msm_ba_debug.h"
+#include "msm_ba_common.h"
+
+#define MSM_BA_DEV_NAME "msm_ba_8064"
+
+#define MSM_BA_MAX_EVENTS 10
+
+int msm_ba_poll(void *instance, struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ poll_wait(filp, &inst->event_handler.wait, wait);
+ if (v4l2_event_pending(&inst->event_handler))
+ rc |= POLLPRI;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_poll);
+
+int msm_ba_querycap(void *instance, struct v4l2_capability *cap)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !cap) {
+ dprintk(BA_ERR,
+ "Invalid input, inst = 0x%p, cap = 0x%p", inst, cap);
+ return -EINVAL;
+ }
+
+ strlcpy(cap->driver, MSM_BA_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, MSM_BA_DEV_NAME, sizeof(cap->card));
+ cap->bus_info[0] = 0;
+ cap->version = MSM_BA_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+ memset(cap->reserved, 0x00, sizeof(cap->reserved));
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_ba_querycap);
+
+int msm_ba_s_parm(void *instance, struct v4l2_streamparm *a)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !a)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_parm);
+
+int msm_ba_enum_input(void *instance, struct v4l2_input *input)
+{
+ struct msm_ba_input *ba_input = NULL;
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !input)
+ return -EINVAL;
+
+ if (input->index >= inst->dev_ctxt->num_inputs)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_input(input->index);
+ if (ba_input) {
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = V4L2_STD_ALL;
+ strlcpy(input->name, ba_input->name, sizeof(input->name));
+ if (ba_input->input_type == BA_INPUT_HDMI ||
+ ba_input->input_type == BA_INPUT_MHL)
+ input->capabilities = V4L2_IN_CAP_CUSTOM_TIMINGS;
+ else
+ input->capabilities = V4L2_IN_CAP_STD;
+ dprintk(BA_DBG, "msm_ba_find_input: name %s", input->name);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_enum_input);
+
+int msm_ba_g_input(void *instance, unsigned int *index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst || !index)
+ return -EINVAL;
+
+ do {
+ /* First find current input */
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ if (ba_input->input_user_type ==
+ BA_INPUT_USERTYPE_KERNEL) {
+ inst->sd_input.index++;
+ continue;
+ }
+ break;
+ }
+ } while (ba_input);
+
+ if (ba_input)
+ *index = inst->sd_input.index;
+ else
+ rc = -ENOENT;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_input);
+
+int msm_ba_s_input(void *instance, unsigned int index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+ int rc_sig = 0;
+
+ if (!inst)
+ return -EINVAL;
+ if (index > inst->dev_ctxt->num_inputs)
+ return -EINVAL;
+
+ /* Find requested input */
+ ba_input = msm_ba_find_input(index);
+ if (!ba_input) {
+ dprintk(BA_ERR, "Could not find input index: %d", index);
+ return -EINVAL;
+ }
+ if (!ba_input->sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (ba_input->in_use &&
+ inst->event_handler.prio == V4L2_PRIORITY_RECORD) {
+ dprintk(BA_WARN, "Input %d in use", index);
+ return -EBUSY;
+ }
+ if (ba_input->ba_out_in_use) {
+ if (inst->ext_ops) {
+ if (inst->restore) {
+ dprintk(BA_DBG, "Stream off in set input: %d",
+ ba_input->bridge_chip_ip);
+ rc_sig = v4l2_subdev_call(ba_input->sd,
+ video, s_stream, 0);
+ if (rc_sig)
+ dprintk(BA_ERR,
+ "%s: Error in stream off. rc_sig %d",
+ __func__, rc_sig);
+ }
+ } else {
+ dprintk(BA_WARN, "Sd %d in use", ba_input->ba_out);
+ return -EBUSY;
+ }
+ }
+ rc = v4l2_subdev_call(ba_input->sd, video, s_routing,
+ ba_input->bridge_chip_ip, 0, 0);
+ if (rc) {
+ dprintk(BA_ERR, "Error: %d setting input: %d",
+ rc, ba_input->bridge_chip_ip);
+ return rc;
+ }
+ msm_ba_reset_ip_in_use_from_sd(ba_input->sd);
+ inst->sd_input.index = index;
+ strlcpy(inst->sd_input.name, ba_input->name,
+ sizeof(inst->sd_input.name));
+ inst->sd = ba_input->sd;
+ ba_input->in_use = 1;
+ /* get current signal status */
+ rc_sig = v4l2_subdev_call(
+ ba_input->sd, video, g_input_status, &ba_input->signal_status);
+ dprintk(BA_DBG, "Set input %s : %d - signal status: %d",
+ ba_input->name, index, ba_input->signal_status);
+ if (!rc_sig && !ba_input->signal_status) {
+ struct v4l2_event sd_event = {
+ .id = 0,
+ .type = V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK};
+ int *ptr = (int *)sd_event.u.data;
+ ptr[0] = index;
+ ptr[1] = ba_input->signal_status;
+ msm_ba_queue_v4l2_event(inst, &sd_event);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_s_input);
+
+int msm_ba_enum_output(void *instance, struct v4l2_output *output)
+{
+ struct msm_ba_input *ba_input = NULL;
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !output)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_output(output->index);
+ if (!ba_input)
+ return -EINVAL;
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->std = V4L2_STD_ALL;
+ strlcpy(output->name, ba_input->sd->name, sizeof(output->name));
+ output->capabilities = V4L2_OUT_CAP_STD;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_enum_output);
+
+int msm_ba_g_output(void *instance, unsigned int *index)
+{
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !index)
+ return -EINVAL;
+
+ *index = inst->sd_output.index;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_output);
+
+int msm_ba_s_output(void *instance, unsigned int index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_output(index);
+ if (ba_input) {
+ if (!ba_input->sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ ba_input->ba_out = index;
+ inst->sd_output.index = index;
+ inst->sd = ba_input->sd;
+ inst->sd_input.index = ba_input->ba_ip_idx;
+ } else {
+ dprintk(BA_ERR, "Could not find output index: %d", index);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_s_output);
+
+int msm_ba_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_enum_fmt);
+
+int msm_ba_s_fmt(void *instance, struct v4l2_format *f)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_fmt);
+
+int msm_ba_g_fmt(void *instance, struct v4l2_format *f)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ struct msm_ba_input *ba_input = NULL;
+ v4l2_std_id new_std = V4L2_STD_UNKNOWN;
+ struct v4l2_dv_timings sd_dv_timings;
+ struct v4l2_subdev_format sd_fmt;
+ int rc = 0;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (!ba_input) {
+ dprintk(BA_ERR, "Could not find input index: %d",
+ inst->sd_input.index);
+ return -EINVAL;
+ }
+ if (ba_input->input_type != BA_INPUT_HDMI) {
+ rc = v4l2_subdev_call(sd, video, querystd, &new_std);
+ if (rc) {
+ dprintk(BA_ERR, "querystd failed %d for sd: %s",
+ rc, sd->name);
+ return -EINVAL;
+ }
+ inst->sd_input.std = new_std;
+ } else {
+ rc = v4l2_subdev_call(sd, video, g_dv_timings, &sd_dv_timings);
+ if (rc) {
+ dprintk(BA_ERR, "g_dv_timings failed %d for sd: %s",
+ rc, sd->name);
+ return -EINVAL;
+ }
+ }
+
+ rc = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
+ if (rc) {
+ dprintk(BA_ERR, "get_fmt failed %d for sd: %s",
+ rc, sd->name);
+ } else {
+ f->fmt.pix.height = sd_fmt.format.height;
+ f->fmt.pix.width = sd_fmt.format.width;
+ switch (sd_fmt.format.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YVYU;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_VYUY;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ break;
+ default:
+ dprintk(BA_ERR, "Unknown sd_mbus_fmt.code 0x%x",
+ sd_fmt.format.code);
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ break;
+ }
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_fmt);
+
+int msm_ba_s_ctrl(void *instance, struct v4l2_control *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return v4l2_s_ctrl(NULL, &inst->ctrl_handler, control);
+}
+EXPORT_SYMBOL(msm_ba_s_ctrl);
+
+int msm_ba_g_ctrl(void *instance, struct v4l2_control *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return v4l2_g_ctrl(&inst->ctrl_handler, control);
+}
+EXPORT_SYMBOL(msm_ba_g_ctrl);
+
+int msm_ba_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_ext_ctrl);
+
+int msm_ba_streamon(void *instance, enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, s_stream, 1);
+ if (rc)
+ dprintk(BA_ERR, "Stream on failed on input: %d",
+ inst->sd_input.index);
+ else
+ msm_ba_set_out_in_use(sd, 1);
+
+ dprintk(BA_DBG, "Stream on: %s : %d",
+ inst->sd_input.name, inst->sd_input.index);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_streamon);
+
+int msm_ba_streamoff(void *instance, enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, s_stream, 0);
+ if (rc)
+ dprintk(BA_ERR, "Stream off failed on input: %d",
+ inst->sd_input.index);
+
+ dprintk(BA_DBG, "Stream off: %s : %d",
+ inst->sd_input.name, inst->sd_input.index);
+ msm_ba_set_out_in_use(sd, 0);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_streamoff);
+
+long msm_ba_private_ioctl(void *instance, int cmd, void *arg)
+{
+ long rc = 0;
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int *s_ioctl = arg;
+
+ dprintk(BA_DBG, "Enter %s with command: 0x%x", __func__, cmd);
+
+ if (!inst)
+ return -EINVAL;
+
+ switch (cmd) {
+ case VIDIOC_HDMI_RX_CEC_S_LOGICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_S_LOGICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_G_PHYSICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_G_PHYSICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_G_CONNECTED: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_G_CONNECTED");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_S_ENABLE: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_S_ENABLE");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ default:
+ dprintk(BA_WARN, "Not a typewriter! Command: 0x%x", cmd);
+ rc = -ENOTTY;
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_private_ioctl);
+
+int msm_ba_save_restore_input(void *instance, enum msm_ba_save_restore_ip sr)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ if (sr == BA_SR_RESTORE_IP &&
+ inst->restore) {
+ dprintk(BA_DBG, "Restoring input: %d",
+ inst->saved_input);
+ rc = v4l2_subdev_call(inst->sd, video, s_routing,
+ inst->saved_input, 0, 0);
+ if (rc)
+ dprintk(BA_ERR, "Failed to restore input: %d",
+ inst->saved_input);
+ msm_ba_reset_ip_in_use_from_sd(inst->sd);
+ ba_input = msm_ba_find_input_from_sd(inst->sd,
+ inst->saved_input);
+ if (ba_input)
+ ba_input->in_use = 1;
+ else
+ dprintk(BA_WARN, "Could not find input %d from sd: %s",
+ inst->saved_input, inst->sd->name);
+ inst->restore = 0;
+ inst->saved_input = BA_IP_MAX;
+ dprintk(BA_DBG, "Stream on from save restore");
+ rc = msm_ba_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ } else if (sr == BA_SR_SAVE_IP) {
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input == NULL) {
+ dprintk(BA_ERR, "Could not find input %d",
+ inst->sd_input.index);
+ } else if (ba_input->ba_out_in_use) {
+ inst->restore = 1;
+ inst->saved_input =
+ msm_ba_find_ip_in_use_from_sd(inst->sd);
+ if (inst->saved_input == BA_IP_MAX) {
+ dprintk(BA_ERR, "Could not find input to save");
+ inst->restore = 0;
+ }
+ dprintk(BA_DBG, "Saving input: %d",
+ inst->saved_input);
+ rc = -EBUSY;
+ }
+ } else {
+ dprintk(BA_DBG, "Nothing to do in save and restore");
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_save_restore_input);
+
+void msm_ba_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+static int msm_ba_register_v4l2_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd)
+{
+ struct video_device *vdev;
+ int rc = 0;
+
+ dprintk(BA_DBG, "Enter %s: v4l2_dev 0x%p, v4l2_subdev 0x%p",
+ __func__, v4l2_dev, sd);
+ if (NULL == v4l2_dev || NULL == sd || !sd->name[0]) {
+ dprintk(BA_ERR, "Invalid input");
+ return -EINVAL;
+ }
+ rc = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (rc < 0) {
+ dprintk(BA_ERR,
+ "%s(%d), V4L2 subdev register failed for %s rc: %d",
+ __func__, __LINE__, sd->name, rc);
+ return rc;
+ }
+ if (sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) {
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ dprintk(BA_ERR, "%s Not enough memory", __func__);
+ return -ENOMEM;
+ }
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = msm_ba_release_subdev_node;
+ rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (rc < 0) {
+ dprintk(BA_ERR, "%s Error registering video device %s",
+ __func__, sd->name);
+ kfree(vdev);
+ } else {
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.dev.major = VIDEO_MAJOR;
+ sd->entity.info.dev.minor = vdev->minor;
+ sd->entity.name = video_device_node_name(vdev);
+#endif
+ sd->devnode = vdev;
+ }
+ }
+ dprintk(BA_DBG, "Exit %s with rc: %d", __func__, rc);
+
+ return rc;
+}
+
+int msm_ba_register_subdev_node(struct v4l2_subdev *sd)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ rc = msm_ba_register_v4l2_subdev(&ba_ctxt->dev_ctxt->v4l2_dev, sd);
+ if (!rc) {
+ ba_ctxt->dev_ctxt->num_ba_subdevs++;
+ msm_ba_add_inputs(sd);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_register_subdev_node);
+
+static void __msm_ba_sd_unregister(struct v4l2_subdev *sub_dev)
+{
+ struct ba_ctxt *ba_ctxt;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ mutex_lock(&ba_ctxt->ba_cs);
+
+ v4l2_device_unregister_subdev(sub_dev);
+ ba_ctxt->dev_ctxt->num_ba_subdevs--;
+ msm_ba_del_inputs(sub_dev);
+
+ dprintk(BA_DBG, "%s(%d), BA Unreg Sub Device : num ba devices %d : %s",
+ __func__, __LINE__,
+ ba_ctxt->dev_ctxt->num_ba_subdevs, sub_dev->name);
+
+ mutex_unlock(&ba_ctxt->ba_cs);
+}
+
+int msm_ba_unregister_subdev_node(struct v4l2_subdev *sub_dev)
+{
+ struct ba_ctxt *ba_ctxt;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ if (!ba_ctxt || !ba_ctxt->dev_ctxt)
+ return -ENODEV;
+ if (!sub_dev)
+ return -EINVAL;
+ __msm_ba_sd_unregister(sub_dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_ba_unregister_subdev_node);
+
+static int msm_ba_setup_event_queue(void *inst,
+ struct video_device *pvdev)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ v4l2_fh_init(&ba_inst->event_handler, pvdev);
+ v4l2_fh_add(&ba_inst->event_handler);
+
+ return rc;
+}
+
+int msm_ba_subscribe_event(void *inst,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ if (!inst || !sub)
+ return -EINVAL;
+
+ rc = v4l2_event_subscribe(&ba_inst->event_handler, sub,
+ MSM_BA_MAX_EVENTS, NULL);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_subscribe_event);
+
+int msm_ba_unsubscribe_event(void *inst,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ if (!inst || !sub)
+ return -EINVAL;
+
+ rc = v4l2_event_unsubscribe(&ba_inst->event_handler, sub);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_unsubscribe_event);
+
+void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_input *ba_input;
+ struct msm_ba_sd_event *ba_sd_event;
+ int bridge_chip_ip;
+
+ if (!sd || !arg) {
+ dprintk(BA_ERR, "%s null v4l2 subdev or arg", __func__);
+ return;
+ }
+
+ bridge_chip_ip = ((int *)((struct v4l2_event *)arg)->u.data)[0];
+ ba_input = msm_ba_find_input_from_sd(sd, bridge_chip_ip);
+ if (!ba_input) {
+ dprintk(BA_WARN, "Could not find input %d from sd: %s",
+ bridge_chip_ip, sd->name);
+ return;
+ }
+
+ ba_sd_event = kzalloc(sizeof(*ba_sd_event), GFP_KERNEL);
+ if (!ba_sd_event) {
+ dprintk(BA_ERR, "%s out of memory", __func__);
+ return;
+ }
+
+ dev_ctxt = get_ba_dev();
+
+ ba_sd_event->sd_event = *(struct v4l2_event *)arg;
+ ((int *)ba_sd_event->sd_event.u.data)[0] = ba_input->ba_ip_idx;
+ mutex_lock(&dev_ctxt->dev_cs);
+ list_add_tail(&ba_sd_event->list, &dev_ctxt->sd_events);
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ schedule_delayed_work(&dev_ctxt->sd_events_work, 0);
+}
+
+void *msm_ba_open(const struct msm_ba_ext_ops *ext_ops)
+{
+ struct msm_ba_inst *inst = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int rc = 0;
+
+ dev_ctxt = get_ba_dev();
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+
+ if (!inst) {
+ dprintk(BA_ERR, "Failed to allocate memory");
+ return NULL;
+ }
+
+ mutex_init(&inst->inst_cs);
+
+ init_waitqueue_head(&inst->kernel_event_queue);
+ inst->state = MSM_BA_DEV_UNINIT_DONE;
+ inst->dev_ctxt = dev_ctxt;
+ rc = msm_ba_ctrl_init(inst);
+ if (rc) {
+ dprintk(BA_WARN, "Failed to initialize controls: %d", rc);
+ msm_ba_ctrl_deinit(inst);
+ }
+
+ if (!list_empty(&(inst->dev_ctxt->v4l2_dev.subdevs)))
+ inst->sd = list_first_entry(&(inst->dev_ctxt->v4l2_dev.subdevs),
+ struct v4l2_subdev, list);
+
+ msm_ba_setup_event_queue(inst, dev_ctxt->vdev);
+
+ mutex_lock(&dev_ctxt->dev_cs);
+ list_add_tail(&inst->list, &dev_ctxt->instances);
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ dev_ctxt->state = BA_DEV_INIT;
+ dev_ctxt->state = BA_DEV_INIT_DONE;
+ inst->state = MSM_BA_DEV_INIT_DONE;
+ inst->sd_input.index = 0;
+ inst->event_handler.prio = V4L2_PRIORITY_DEFAULT;
+
+ inst->debugfs_root =
+ msm_ba_debugfs_init_inst(inst, dev_ctxt->debugfs_root);
+
+ inst->ext_ops = ext_ops;
+
+ return inst;
+}
+EXPORT_SYMBOL(msm_ba_open);
+
+int msm_ba_close(void *instance)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_inst *temp;
+ struct msm_ba_dev *dev_ctxt;
+ struct list_head *ptr;
+ struct list_head *next;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ dev_ctxt = inst->dev_ctxt;
+ mutex_lock(&dev_ctxt->dev_cs);
+
+ list_for_each_safe(ptr, next, &dev_ctxt->instances) {
+ temp = list_entry(ptr, struct msm_ba_inst, list);
+ if (temp == inst)
+ list_del(&inst->list);
+ }
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ msm_ba_ctrl_deinit(inst);
+
+ v4l2_fh_del(&inst->event_handler);
+ v4l2_fh_exit(&inst->event_handler);
+
+ debugfs_remove_recursive(inst->debugfs_root);
+
+ dprintk(BA_DBG, "Closed BA instance: %p", inst);
+ kfree(inst);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_close);
diff --git a/drivers/video/msm/ba/msm_ba_common.c b/drivers/video/msm/ba/msm_ba_common.c
new file mode 100644
index 000000000000..cc8eb2da3e3b
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_common.c
@@ -0,0 +1,645 @@
+/* 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
+ * 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/jiffies.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
+#include "msm_ba_debug.h"
+#include "msm_ba_common.h"
+
+static struct msm_ba_ctrl msm_ba_ctrls[] = {
+ {
+ .id = MSM_BA_PRIV_SD_NODE_ADDR,
+ .name = "Sub-device Node Address",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 16,
+ .default_value = 0,
+ .step = 2,
+ .menu_skip_mask = 0,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .qmenu = NULL,
+ },
+ {
+ .id = MSM_BA_PRIV_FPS,
+ .name = "FPS in Q16 format",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 0x7fffffff,
+ .default_value = 60 << 16,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .qmenu = NULL,
+ },
+};
+
+#define BA_NUM_CTRLS ARRAY_SIZE(msm_ba_ctrls)
+
+/* Assuming den is not zero, max 32 bits */
+#define BA_FRAC_TO_Q16(q, num, den) { \
+ uint32_t pwr; \
+ pwr = ilog2(den); \
+ (q) = (num) << (16 - pwr); \
+ }
+
+struct msm_ba_dev *get_ba_dev(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ mutex_lock(&ba_ctxt->ba_cs);
+ dev_ctxt = ba_ctxt->dev_ctxt;
+ mutex_unlock(&ba_ctxt->ba_cs);
+
+ return dev_ctxt;
+}
+
+void msm_ba_queue_v4l2_event(struct msm_ba_inst *inst,
+ struct v4l2_event *sd_event)
+{
+ v4l2_event_queue_fh(&inst->event_handler, sd_event);
+ wake_up(&inst->kernel_event_queue);
+}
+
+static void msm_ba_print_event(struct v4l2_event *sd_event)
+{
+ switch (sd_event->type) {
+ case V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED:
+ dprintk(BA_DBG, "Port settings changed for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK:
+ dprintk(BA_DBG, "Signal in lock for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK:
+ dprintk(BA_DBG, "Signal lost lock for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SOURCE_CHANGE:
+ dprintk(BA_DBG, "Video source change 0x%x",
+ ((int *)sd_event->u.data)[1]);
+ break;
+ case V4L2_EVENT_MSM_BA_HDMI_HPD:
+ dprintk(BA_DBG, "HDMI hotplug detected!");
+ break;
+ case V4L2_EVENT_MSM_BA_HDMI_CEC_MESSAGE:
+ dprintk(BA_DBG, "HDMI CEC message!");
+ break;
+ case V4L2_EVENT_MSM_BA_CP:
+ dprintk(BA_DBG, "Content protection detected!");
+ break;
+ case V4L2_EVENT_MSM_BA_CABLE_DETECT:
+ dprintk(BA_DBG, "Cable detected: %d on ip_idx %d",
+ ((int *)sd_event->u.data)[1],
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_ERROR:
+ dprintk(BA_DBG, "Subdev error %d!",
+ ((int *)sd_event->u.data)[1]);
+ break;
+ default:
+ dprintk(BA_ERR, "Unknown event: 0x%x", sd_event->type);
+ break;
+ }
+}
+
+static void msm_ba_signal_sessions_event(struct v4l2_event *sd_event)
+{
+ struct msm_ba_inst *inst = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int *ptr;
+
+ msm_ba_print_event(sd_event);
+ dev_ctxt = get_ba_dev();
+ ptr = (int *)sd_event->u.data;
+
+ list_for_each_entry(inst, &(dev_ctxt->instances), list) {
+ if (inst->ext_ops && inst->ext_ops->msm_ba_cb)
+ inst->ext_ops->msm_ba_cb(
+ inst, sd_event->id, (void *)&ptr[1]);
+ else
+ msm_ba_queue_v4l2_event(inst, sd_event);
+ }
+}
+
+void msm_ba_subdev_event_hndlr_delayed(struct work_struct *work)
+{
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_sd_event *ba_sd_event = NULL;
+ struct msm_ba_sd_event *ba_sd_event_tmp = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ mutex_lock(&dev_ctxt->dev_cs);
+ if (!list_empty(&dev_ctxt->sd_events)) {
+ list_for_each_entry_safe(ba_sd_event, ba_sd_event_tmp,
+ &(dev_ctxt->sd_events), list) {
+ msm_ba_signal_sessions_event(&ba_sd_event->sd_event);
+ list_del(&ba_sd_event->list);
+ kfree(ba_sd_event);
+ break;
+ }
+ } else {
+ dprintk(BA_ERR, "%s - queue empty!!!", __func__);
+ }
+ mutex_unlock(&dev_ctxt->dev_cs);
+}
+
+struct v4l2_subdev *msm_ba_sd_find(const char *name)
+{
+ struct v4l2_subdev *sd = NULL;
+ struct v4l2_subdev *sd_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+ if (!list_empty(&(dev_ctxt->v4l2_dev.subdevs))) {
+ list_for_each_entry(sd, &(dev_ctxt->v4l2_dev.subdevs), list)
+ if (!strcmp(name, sd->name)) {
+ sd_out = sd;
+ break;
+ }
+ }
+ return sd_out;
+}
+
+void msm_ba_add_inputs(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_input_config *msm_ba_inp_cfg = NULL;
+ int i;
+ int str_length = 0;
+ int rc;
+ int start_index = 0;
+ int end_index = 0;
+ int dev_id = 0;
+
+ dev_ctxt = get_ba_dev();
+ if (!list_empty(&dev_ctxt->inputs))
+ start_index = dev_ctxt->num_inputs;
+
+ msm_ba_inp_cfg = dev_ctxt->msm_ba_inp_cfg;
+ dev_id = msm_ba_inp_cfg[start_index].ba_out;
+ end_index = dev_ctxt->num_config_inputs;
+ for (i = start_index; i < end_index; i++) {
+ str_length = strlen(msm_ba_inp_cfg[i].sd_name);
+ rc = memcmp(sd->name, msm_ba_inp_cfg[i].sd_name, str_length);
+ if (!rc && dev_id == msm_ba_inp_cfg[i].ba_out) {
+ input = kzalloc(sizeof(*input), GFP_KERNEL);
+
+ if (!input) {
+ dprintk(BA_ERR, "Failed to allocate memory");
+ break;
+ }
+ input->input_type = msm_ba_inp_cfg[i].input_type;
+ strlcpy(input->name, msm_ba_inp_cfg[i].name,
+ sizeof(input->name));
+ input->bridge_chip_ip = msm_ba_inp_cfg[i].ba_ip;
+ input->ba_out = msm_ba_inp_cfg[i].ba_out;
+ input->ba_node_addr = msm_ba_inp_cfg[i].ba_node;
+ input->ba_ip_idx = i;
+ input->input_user_type =
+ msm_ba_inp_cfg[i].input_user_type;
+ input->sd = sd;
+ list_add_tail(&input->list, &dev_ctxt->inputs);
+ dev_ctxt->num_inputs++;
+ dprintk(BA_DBG, "Add input: name %s on %d",
+ input->name, input->ba_out);
+ }
+ }
+}
+
+void msm_ba_del_inputs(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct list_head *ptr;
+ struct list_head *next;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ list_for_each_safe(ptr, next, &(dev_ctxt->inputs)) {
+ input = list_entry(ptr, struct msm_ba_input, list);
+ if (input->sd == sd) {
+ list_del(&input->list);
+ kfree(input);
+ }
+ }
+}
+
+void msm_ba_set_out_in_use(struct v4l2_subdev *sd, int on)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd)
+ input->ba_out_in_use = on;
+ }
+}
+
+int msm_ba_find_ip_in_use_from_sd(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int ba_ip = BA_IP_MAX;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->in_use) {
+ ba_ip = input->bridge_chip_ip;
+ break;
+ }
+ }
+ return ba_ip;
+}
+
+void msm_ba_reset_ip_in_use_from_sd(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->in_use) {
+ input->in_use = 0;
+ break;
+ }
+ }
+}
+
+struct msm_ba_input *msm_ba_find_input_from_sd(struct v4l2_subdev *sd,
+ int bridge_chip_ip)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->bridge_chip_ip == bridge_chip_ip) {
+ input_out = input;
+ break;
+ }
+ }
+ return input_out;
+}
+
+struct msm_ba_input *msm_ba_find_input(int ba_input_idx)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->ba_ip_idx == ba_input_idx) {
+ input_out = input;
+ break;
+ }
+ }
+ return input_out;
+}
+
+struct msm_ba_input *msm_ba_find_output(int ba_output)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list) {
+ if (input->ba_out == ba_output) {
+ input_out = input;
+ break;
+ }
+ }
+ }
+ return input_out;
+}
+
+int msm_ba_g_fps(void *instance, int *fps_q16)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ struct v4l2_subdev_frame_interval sd_frame_int;
+ int rc = 0;
+
+ if (!inst || !fps_q16)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, g_frame_interval, &sd_frame_int);
+ if (rc) {
+ dprintk(BA_ERR, "get frame interval failed %d for sd: %s",
+ rc, sd->name);
+ } else {
+ /* subdevice returns frame interval not fps! */
+ if (sd_frame_int.interval.numerator) {
+ BA_FRAC_TO_Q16(*fps_q16,
+ sd_frame_int.interval.denominator,
+ sd_frame_int.interval.numerator);
+ } else {
+ *fps_q16 =
+ sd_frame_int.interval.denominator << 16;
+ }
+ }
+ return rc;
+}
+
+static int msm_ba_try_get_ctrl(struct msm_ba_inst *inst,
+ struct v4l2_ctrl *ctrl)
+{
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ dprintk(BA_DBG, "%s ctrl->id: 0x%x", __func__, ctrl->id);
+
+ switch (ctrl->id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ ctrl->val = ba_input->ba_node_addr;
+ dprintk(BA_DBG,
+ "%s: SD NODE ADDR ctrl->id:0x%x ctrl->val:%d",
+ __func__, ctrl->id, ctrl->val);
+ } else {
+ dprintk(BA_ERR, "%s Could not find input",
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ case MSM_BA_PRIV_FPS:
+ rc = msm_ba_g_fps(inst, &ctrl->val);
+ break;
+ default:
+ dprintk(BA_ERR, "%s id: 0x%x not supported",
+ __func__, ctrl->id);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int msm_ba_try_set_ctrl(struct msm_ba_inst *inst,
+ struct v4l2_ctrl *ctrl)
+{
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ dprintk(BA_DBG, "%s ctrl->id: 0x%x", __func__, ctrl->id);
+
+ switch (ctrl->id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ ba_input->ba_node_addr = ctrl->val;
+ dprintk(BA_DBG,
+ "%s: SD NODE ADDR ctrl->id:0x%x node_addr:%d",
+ __func__, ctrl->id, ba_input->ba_node_addr);
+ } else {
+ dprintk(BA_ERR, "%s Could not find input",
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ default:
+ dprintk(BA_ERR, "%s id: 0x%x not supported",
+ __func__, ctrl->id);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int msm_ba_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int rc = 0;
+ int c = 0;
+ struct msm_ba_inst *inst = container_of(ctrl->handler,
+ struct msm_ba_inst, ctrl_handler);
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ for (c = 0; c < ctrl->ncontrols; ++c) {
+ if (ctrl->cluster[c]->is_new) {
+ rc = msm_ba_try_set_ctrl(inst, ctrl->cluster[c]);
+ if (rc) {
+ dprintk(BA_ERR, "Failed setting 0x%x",
+ ctrl->cluster[c]->id);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int msm_ba_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int rc = 0;
+ int c = 0;
+ struct msm_ba_inst *inst = container_of(ctrl->handler,
+ struct msm_ba_inst, ctrl_handler);
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ for (c = 0; c < master->ncontrols; c++) {
+ if (master->cluster[c]->id == ctrl->id) {
+ rc = msm_ba_try_get_ctrl(inst, ctrl);
+ if (rc) {
+ dprintk(BA_ERR, "Failed getting 0x%x",
+ ctrl->id);
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+
+static const struct v4l2_ctrl_ops msm_ba_ctrl_ops = {
+
+ .g_volatile_ctrl = msm_ba_op_g_volatile_ctrl,
+ .s_ctrl = msm_ba_op_s_ctrl,
+};
+
+const struct v4l2_ctrl_ops *msm_ba_get_ctrl_ops(void)
+{
+ return &msm_ba_ctrl_ops;
+}
+
+static struct v4l2_ctrl **msm_ba_get_super_cluster(struct msm_ba_inst *inst,
+ int *size)
+{
+ int c = 0;
+ int sz = 0;
+ struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) *
+ BA_NUM_CTRLS, GFP_KERNEL);
+
+ if (!size || !cluster || !inst)
+ return NULL;
+
+ for (c = 0; c < BA_NUM_CTRLS; c++)
+ cluster[sz++] = inst->ctrls[c];
+
+ *size = sz;
+ return cluster;
+}
+
+/*
+ * Controls init function.
+ * Caller is expected to call deinit in case of failure.
+ */
+int msm_ba_ctrl_init(struct msm_ba_inst *inst)
+{
+ int idx = 0;
+ struct v4l2_ctrl_config ctrl_cfg = {0};
+ int rc = 0;
+ int cluster_size = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s - invalid instance", __func__);
+ return -EINVAL;
+ }
+
+ inst->ctrls = kzalloc(sizeof(struct v4l2_ctrl *) * BA_NUM_CTRLS,
+ GFP_KERNEL);
+ if (!inst->ctrls) {
+ dprintk(BA_ERR, "%s - failed to allocate ctrl", __func__);
+ return -ENOMEM;
+ }
+
+ rc = v4l2_ctrl_handler_init(&inst->ctrl_handler, BA_NUM_CTRLS);
+
+ if (rc) {
+ dprintk(BA_ERR, "CTRL ERR: Control handler init failed, %d",
+ inst->ctrl_handler.error);
+ return rc;
+ }
+ for (; idx < BA_NUM_CTRLS; idx++) {
+ struct v4l2_ctrl *ctrl = NULL;
+ if (BA_IS_PRIV_CTRL(msm_ba_ctrls[idx].id)) {
+ /* add private control */
+ ctrl_cfg.def = msm_ba_ctrls[idx].default_value;
+ ctrl_cfg.flags = 0;
+ ctrl_cfg.id = msm_ba_ctrls[idx].id;
+ ctrl_cfg.max = msm_ba_ctrls[idx].maximum;
+ ctrl_cfg.min = msm_ba_ctrls[idx].minimum;
+ ctrl_cfg.menu_skip_mask =
+ msm_ba_ctrls[idx].menu_skip_mask;
+ ctrl_cfg.name = msm_ba_ctrls[idx].name;
+ ctrl_cfg.ops = &msm_ba_ctrl_ops;
+ ctrl_cfg.step = msm_ba_ctrls[idx].step;
+ ctrl_cfg.type = msm_ba_ctrls[idx].type;
+ ctrl_cfg.qmenu = msm_ba_ctrls[idx].qmenu;
+
+ ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler,
+ &ctrl_cfg, NULL);
+ } else {
+ if (msm_ba_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) {
+ ctrl = v4l2_ctrl_new_std_menu(
+ &inst->ctrl_handler,
+ &msm_ba_ctrl_ops,
+ msm_ba_ctrls[idx].id,
+ msm_ba_ctrls[idx].maximum,
+ msm_ba_ctrls[idx].menu_skip_mask,
+ msm_ba_ctrls[idx].default_value);
+ } else {
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
+ &msm_ba_ctrl_ops,
+ msm_ba_ctrls[idx].id,
+ msm_ba_ctrls[idx].minimum,
+ msm_ba_ctrls[idx].maximum,
+ msm_ba_ctrls[idx].step,
+ msm_ba_ctrls[idx].default_value);
+ }
+ }
+
+ if (!ctrl) {
+ dprintk(BA_ERR, "%s - invalid ctrl", __func__);
+ return -EINVAL;
+ }
+
+ rc = inst->ctrl_handler.error;
+ if (rc) {
+ dprintk(BA_ERR,
+ "Error adding ctrl (%s) to ctrl handle, %d",
+ msm_ba_ctrls[idx].name,
+ inst->ctrl_handler.error);
+ return rc;
+ }
+
+ switch (msm_ba_ctrls[idx].id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ case MSM_BA_PRIV_FPS:
+ ctrl->flags |= msm_ba_ctrls[idx].flags;
+ break;
+ }
+
+ inst->ctrls[idx] = ctrl;
+ }
+
+ /* Construct a super cluster of all controls */
+ inst->cluster = msm_ba_get_super_cluster(inst, &cluster_size);
+ if (!inst->cluster || !cluster_size) {
+ dprintk(BA_WARN,
+ "Failed to setup super cluster");
+ return -EINVAL;
+ }
+ v4l2_ctrl_cluster(cluster_size, inst->cluster);
+
+ return rc;
+}
+
+void msm_ba_ctrl_deinit(struct msm_ba_inst *inst)
+{
+ kfree(inst->ctrls);
+ kfree(inst->cluster);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+}
diff --git a/drivers/video/msm/ba/msm_ba_common.h b/drivers/video/msm/ba/msm_ba_common.h
new file mode 100644
index 000000000000..64ef2ab7537e
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_common.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_BA_COMMON_H_
+#define _MSM_BA_COMMON_H_
+
+#include "msm_ba_internal.h"
+
+#define BA_IS_PRIV_CTRL(idx) (\
+ (V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_USER) && \
+ V4L2_CTRL_DRIVER_PRIV(idx))
+
+struct msm_ba_dev *get_ba_dev(void);
+void msm_ba_queue_v4l2_event(struct msm_ba_inst *inst,
+ struct v4l2_event *sd_event);
+struct v4l2_subdev *msm_ba_sd_find(const char *name);
+void msm_ba_add_inputs(struct v4l2_subdev *sd);
+void msm_ba_del_inputs(struct v4l2_subdev *sd);
+void msm_ba_set_out_in_use(struct v4l2_subdev *sd, int on);
+int msm_ba_find_ip_in_use_from_sd(struct v4l2_subdev *sd);
+void msm_ba_reset_ip_in_use_from_sd(struct v4l2_subdev *sd);
+struct msm_ba_input *msm_ba_find_input_from_sd(struct v4l2_subdev *sd,
+ int bridge_chip_ip);
+struct msm_ba_input *msm_ba_find_input(int ba_input_idx);
+struct msm_ba_input *msm_ba_find_output(int ba_output);
+int msm_ba_g_fps(void *instance, int *fps_q16);
+int msm_ba_ctrl_init(struct msm_ba_inst *inst);
+void msm_ba_ctrl_deinit(struct msm_ba_inst *inst);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_ba_debug.c b/drivers/video/msm/ba/msm_ba_debug.c
new file mode 100644
index 000000000000..aa5109eb8e64
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_debug.c
@@ -0,0 +1,223 @@
+/* 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
+ * 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 "msm_ba_debug.h"
+
+#define MAX_DBG_BUF_SIZE 1008
+
+int msm_ba_debug = BA_ERR | BA_WARN;
+int msm_ba_debug_out = BA_OUT_PRINTK;
+
+struct debug_buffer {
+ char ptr[MAX_DBG_BUF_SIZE];
+ char *curr;
+ u32 filled_size;
+};
+
+#define INIT_DBG_BUF(__buf) ({ \
+ __buf->curr = __buf->ptr;\
+ __buf->filled_size = 0; \
+})
+
+static int dev_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...)
+{
+ va_list args;
+ u32 size = 0;
+ size_t buf_size = 0;
+
+ if (MAX_DBG_BUF_SIZE - 1 > buffer->filled_size) {
+ buf_size = MAX_DBG_BUF_SIZE - 1 - buffer->filled_size;
+ va_start(args, fmt);
+ size = vscnprintf(buffer->curr, buf_size, fmt, args);
+ va_end(args);
+ buffer->curr += size;
+ buffer->filled_size += size;
+ }
+ return size;
+}
+
+static ssize_t dev_info_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_ba_dev *dev_ctxt = file->private_data;
+ struct debug_buffer *dbg_buf = NULL;
+ ssize_t size = 0;
+
+ if (!dev_ctxt) {
+ dprintk(BA_ERR, "Invalid params, dev: 0x%p", dev_ctxt);
+ return 0;
+ }
+
+ dbg_buf = kmalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+ if (dbg_buf == NULL)
+ return 0;
+
+ INIT_DBG_BUF(dbg_buf);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "DEV: 0x%p", dev_ctxt);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "state: %d", dev_ctxt->state);
+
+ size = simple_read_from_buffer(buf, count, ppos,
+ dbg_buf->ptr, dbg_buf->filled_size);
+
+ kfree(dbg_buf);
+
+ return size;
+}
+
+static const struct file_operations dev_info_fops = {
+ .open = dev_info_open,
+ .read = dev_info_read,
+};
+
+struct dentry *msm_ba_debugfs_init_drv(void)
+{
+ bool ok = false;
+ struct dentry *dir = debugfs_create_dir(BA_DBG_LABEL, NULL);
+ struct ba_ctxt *ba_ctxt;
+
+ if (IS_ERR_OR_NULL(dir)) {
+ dir = NULL;
+ goto failed_create_dir;
+ }
+
+#define __debugfs_create(__type, __name, __value) ({ \
+ struct dentry *f = debugfs_create_##__type(__name, S_IRUGO | S_IWUSR, \
+ dir, __value); \
+ if (IS_ERR_OR_NULL(f)) { \
+ dprintk(BA_ERR, "Failed creating debugfs file '%pd/%s'", \
+ dir, __name); \
+ f = NULL; \
+ } \
+ f; \
+})
+
+ ok =
+ __debugfs_create(x32, "debug_level", &msm_ba_debug) &&
+ __debugfs_create(u32, "debug_output", &msm_ba_debug_out);
+
+#undef __debugfs_create
+
+ if (!ok)
+ goto failed_create_dir;
+
+ return dir;
+
+failed_create_dir:
+ if (dir) {
+ ba_ctxt = msm_ba_get_ba_context();
+ debugfs_remove_recursive(ba_ctxt->debugfs_root);
+ }
+ return NULL;
+}
+
+struct dentry *msm_ba_debugfs_init_dev(struct msm_ba_dev *dev_ctxt,
+ struct dentry *parent)
+{
+ struct dentry *dir = NULL;
+ char debugfs_name[MAX_DEBUGFS_NAME];
+
+ if (!dev_ctxt) {
+ dprintk(BA_ERR, "Invalid params, core: %p", dev_ctxt);
+ goto failed_create_dir;
+ }
+
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "dev_%p", dev_ctxt);
+ dir = debugfs_create_dir(debugfs_name, parent);
+ if (!dir) {
+ dprintk(BA_ERR, "Failed to create debugfs for msm_ba");
+ goto failed_create_dir;
+ }
+ if (!debugfs_create_file("info", S_IRUGO, dir, dev_ctxt,
+ &dev_info_fops)) {
+ dprintk(BA_ERR, "debugfs_create_file: fail");
+ goto failed_create_dir;
+ }
+failed_create_dir:
+ return dir;
+}
+
+static int inst_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t inst_info_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_ba_inst *inst = file->private_data;
+ struct debug_buffer *dbg_buf = NULL;
+ ssize_t size = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "Invalid params, dev: %p", inst);
+ return 0;
+ }
+
+ dbg_buf = kmalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+ if (dbg_buf == NULL)
+ return 0;
+
+ INIT_DBG_BUF(dbg_buf);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "INSTANCE: %p (%s)", inst,
+ "BA device");
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "dev: %p", inst->dev_ctxt);
+ write_str(dbg_buf, "state: %d", inst->state);
+
+ size = simple_read_from_buffer(buf, count, ppos,
+ dbg_buf->ptr, dbg_buf->filled_size);
+
+ kfree(dbg_buf);
+
+ return size;
+}
+
+static const struct file_operations inst_info_fops = {
+ .open = inst_info_open,
+ .read = inst_info_read,
+};
+
+struct dentry *msm_ba_debugfs_init_inst(struct msm_ba_inst *inst,
+ struct dentry *parent)
+{
+ struct dentry *dir = NULL;
+ char debugfs_name[MAX_DEBUGFS_NAME];
+
+ if (!inst) {
+ dprintk(BA_ERR, "Invalid params, inst: %p", inst);
+ goto failed_create_dir;
+ }
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst);
+ dir = debugfs_create_dir(debugfs_name, parent);
+ if (!dir) {
+ dprintk(BA_ERR, "Failed to create debugfs for msm_ba");
+ goto failed_create_dir;
+ }
+ if (!debugfs_create_file("info", S_IRUGO, dir, inst, &inst_info_fops)) {
+ dprintk(BA_ERR, "debugfs_create_file: fail");
+ goto failed_create_dir;
+ }
+ inst->debug.pdata[SESSION_INIT].sampling = true;
+failed_create_dir:
+ return dir;
+}
diff --git a/drivers/video/msm/ba/msm_ba_debug.h b/drivers/video/msm/ba/msm_ba_debug.h
new file mode 100644
index 000000000000..baabb712cc58
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_debug.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_BA_DEBUG__
+#define __MSM_BA_DEBUG__
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include "msm_ba_internal.h"
+
+#ifndef BA_DBG_LABEL
+#define BA_DBG_LABEL "msm_ba"
+#endif
+
+#define BA_DBG_TAG BA_DBG_LABEL "(%d): %4s: "
+
+/* To enable messages OR these values and
+ * echo the result to debugfs file.
+ *
+ * To enable all messages set debug_level = 0x001F
+ */
+
+enum ba_msg_prio {
+ BA_ERR = 0x0001,
+ BA_WARN = 0x0002,
+ BA_INFO = 0x0004,
+ BA_DBG = 0x0008,
+ BA_PROF = 0x0010
+};
+
+enum ba_msg_out {
+ BA_OUT_PRINTK = 0,
+ BA_OUT_FTRACE
+};
+
+extern int msm_ba_debug;
+extern int msm_ba_debug_out;
+
+#define BA_MSG_PRIO2STRING(__level) ({ \
+ char *__str; \
+ \
+ __str = (__level == BA_ERR ? "err" : \
+ (__level == BA_WARN ? "warn" : \
+ (__level == BA_INFO ? "info" : \
+ (__level == BA_DBG ? "dbg" : \
+ (__level == BA_PROF ? "prof" : "????"))))); \
+ \
+ __str; \
+ })
+
+#define dprintk(__level, __fmt, arg...) \
+ do { \
+ if (msm_ba_debug & __level) { \
+ if (msm_ba_debug_out == BA_OUT_PRINTK) { \
+ pr_info(BA_DBG_TAG __fmt "\n", \
+ __LINE__, \
+ BA_MSG_PRIO2STRING(__level), \
+ ## arg); \
+ } else if (msm_ba_debug_out == BA_OUT_FTRACE) { \
+ trace_printk(KERN_DEBUG BA_DBG_TAG __fmt "\n", \
+ __LINE__, \
+ BA_MSG_PRIO2STRING(__level), \
+ ## arg); \
+ } \
+ } \
+ } while (0)
+
+
+struct dentry *msm_ba_debugfs_init_drv(void);
+struct dentry *msm_ba_debugfs_init_dev(struct msm_ba_dev *dev_ctxt,
+ struct dentry *parent);
+struct dentry *msm_ba_debugfs_init_inst(struct msm_ba_inst *inst,
+ struct dentry *parent);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_ba_internal.h b/drivers/video/msm/ba/msm_ba_internal.h
new file mode 100644
index 000000000000..bd52e8e400ce
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_internal.h
@@ -0,0 +1,220 @@
+/* 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_BA_INTERNAL_H_
+#define _MSM_BA_INTERNAL_H_
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include <media/msm_ba.h>
+
+#define MSM_BA_DRV_NAME "msm_ba_driver"
+
+#define MSM_BA_VERSION KERNEL_VERSION(0, 0, 1)
+
+#define MAX_NAME_LENGTH 64
+
+#define MAX_DEBUGFS_NAME MAX_NAME_LENGTH
+
+#define DEFAULT_WIDTH 720
+#define DEFAULT_HEIGHT 507
+
+enum ba_dev_state {
+ BA_DEV_UNINIT = 0,
+ BA_DEV_LOADED,
+ BA_DEV_INIT,
+ BA_DEV_INIT_DONE,
+ BA_DEV_INVALID
+};
+
+enum instance_state {
+ MSM_BA_DEV_UNINIT_DONE = 0x0001,
+ MSM_BA_DEV_INIT,
+ MSM_BA_DEV_INIT_DONE,
+ MSM_BA_OPEN,
+ MSM_BA_OPEN_DONE,
+ MSM_BA_START,
+ MSM_BA_START_DONE,
+ MSM_BA_STOP,
+ MSM_BA_STOP_DONE,
+ MSM_BA_CLOSE,
+ MSM_BA_CLOSE_DONE,
+ MSM_BA_DEV_UNINIT,
+ MSM_BA_DEV_INVALID
+};
+
+struct ba_ctxt {
+ struct mutex ba_cs;
+ struct msm_ba_dev *dev_ctxt;
+ struct dentry *debugfs_root;
+};
+
+enum profiling_points {
+ SYS_INIT = 0,
+ SESSION_INIT,
+ MAX_PROFILING_POINTS
+};
+
+struct profile_data {
+ int start;
+ int stop;
+ int cumulative;
+ char name[64];
+ int sampling;
+ int average;
+};
+
+struct msm_ba_debug {
+ struct profile_data pdata[MAX_PROFILING_POINTS];
+ int profile;
+ int samples;
+};
+
+struct msm_ba_dev_capability {
+ u32 capability_set;
+};
+
+enum msm_ba_ip_type {
+ BA_INPUT_CVBS = 0,
+ BA_INPUT_COMPONENT,
+ BA_INPUT_YC,
+ BA_INPUT_RGB,
+ BA_INPUT_HDMI,
+ BA_INPUT_MHL,
+ BA_INPUT_DVI,
+ BA_INPUT_TTL,
+ BA_INPUT_MAX = 0xffffffff
+};
+
+enum msm_ba_input_usr_type {
+ BA_INPUT_USERTYPE_KERNEL = 0,
+ BA_INPUT_USERTYPE_USER,
+ BA_INPUT_USERTYPE_MAX = 0xffffffff
+};
+
+struct msm_ba_input_config {
+ enum msm_ba_ip_type input_type;
+ const char *name;
+ int ba_ip;
+ int ba_out;
+ const char *sd_name;
+ int ba_node;
+ enum msm_ba_input_usr_type input_user_type;
+};
+
+struct msm_ba_sd_event {
+ struct list_head list;
+ struct v4l2_event sd_event;
+};
+
+struct msm_ba_input {
+ struct list_head list;
+ enum msm_ba_ip_type input_type;
+ unsigned int name_index;
+ char name[32];
+ int bridge_chip_ip;
+ int ba_node_addr;
+ int ba_out;
+ int ba_ip_idx;
+ struct v4l2_subdev *sd;
+ int signal_status;
+ int in_use;
+ int ba_out_in_use;
+ enum msm_ba_input_usr_type input_user_type;
+};
+
+struct msm_ba_dev {
+ struct mutex dev_cs;
+
+ enum ba_dev_state state;
+
+ struct list_head inputs;
+ uint32_t num_inputs;
+
+ /* V4L2 Framework */
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev;
+ struct media_device mdev;
+
+ struct list_head instances;
+
+ /* BA v4l2 sub devs */
+ uint32_t num_ba_subdevs;
+ struct list_head sd_events;
+ struct delayed_work sd_events_work;
+
+ /* BA input config list */
+ struct msm_ba_input_config *msm_ba_inp_cfg;
+ uint32_t num_config_inputs;
+
+ struct dentry *debugfs_root;
+};
+
+struct msm_ba_inst {
+ struct list_head list;
+ struct mutex inst_cs;
+ struct msm_ba_dev *dev_ctxt;
+
+ struct v4l2_input sd_input;
+ struct v4l2_output sd_output;
+ struct v4l2_subdev *sd;
+ int state;
+ int saved_input;
+ int restore;
+
+ struct v4l2_fh event_handler;
+ wait_queue_head_t kernel_event_queue;
+
+ struct v4l2_ctrl **cluster;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl **ctrls;
+
+ struct msm_ba_debug debug;
+ struct dentry *debugfs_root;
+
+ const struct msm_ba_ext_ops *ext_ops;
+};
+
+struct msm_ba_ctrl {
+ u32 id;
+ char name[MAX_NAME_LENGTH];
+ enum v4l2_ctrl_type type;
+ s32 minimum;
+ s32 maximum;
+ s32 default_value;
+ u32 step;
+ u32 menu_skip_mask;
+ u32 flags;
+ const char * const *qmenu;
+};
+
+struct ba_ctxt *msm_ba_get_ba_context(void);
+
+void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg);
+void msm_ba_subdev_event_hndlr_delayed(struct work_struct *work);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_v4l2_ba.c b/drivers/video/msm/ba/msm_v4l2_ba.c
new file mode 100644
index 000000000000..0cd3fc3b238f
--- /dev/null
+++ b/drivers/video/msm/ba/msm_v4l2_ba.c
@@ -0,0 +1,615 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/msm_ba.h>
+
+#include "msm_ba_internal.h"
+#include "msm_ba_debug.h"
+
+#define BASE_DEVICE_NUMBER 35
+
+static struct ba_ctxt *gp_ba_ctxt;
+
+struct ba_ctxt *msm_ba_get_ba_context(void)
+{
+ return gp_ba_ctxt;
+}
+
+void msm_ba_set_ba_context(struct ba_ctxt *ba_ctxt)
+{
+ gp_ba_ctxt = ba_ctxt;
+}
+
+static inline struct msm_ba_inst *get_ba_inst(struct file *filp, void *fh)
+{
+ return container_of(filp->private_data,
+ struct msm_ba_inst, event_handler);
+}
+
+static int msm_ba_v4l2_open(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ struct msm_ba_inst *ba_inst;
+
+ ba_inst = msm_ba_open(NULL);
+ if (!ba_inst) {
+ dprintk(BA_ERR,
+ "Failed to create video instance");
+ return -ENOMEM;
+ }
+ clear_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags);
+ filp->private_data = &(ba_inst->event_handler);
+ return 0;
+}
+
+static int msm_ba_v4l2_close(struct file *filp)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst;
+
+ ba_inst = get_ba_inst(filp, NULL);
+
+ rc = msm_ba_close(ba_inst);
+ return rc;
+}
+
+static int msm_ba_v4l2_querycap(struct file *filp, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(filp, fh);
+
+ return msm_ba_querycap((void *)ba_inst, cap);
+}
+
+int msm_ba_v4l2_enum_input(struct file *file, void *fh,
+ struct v4l2_input *input)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_input((void *)ba_inst, input);
+}
+
+int msm_ba_v4l2_g_input(struct file *file, void *fh,
+ unsigned int *index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_input((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_s_input(struct file *file, void *fh,
+ unsigned int index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_input((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_enum_output(struct file *file, void *fh,
+ struct v4l2_output *output)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_output((void *)ba_inst, output);
+}
+
+int msm_ba_v4l2_g_output(struct file *file, void *fh,
+ unsigned int *index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_output((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_s_output(struct file *file, void *fh,
+ unsigned int index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_output((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_s_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_s_ext_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_ext_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_streamon((void *)ba_inst, i);
+}
+
+int msm_ba_v4l2_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_streamoff((void *)ba_inst, i);
+}
+
+static int msm_ba_v4l2_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct msm_ba_inst *ba_inst = container_of(fh,
+ struct msm_ba_inst, event_handler);
+
+ return msm_ba_subscribe_event((void *)ba_inst, sub);
+}
+
+static int msm_ba_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct msm_ba_inst *ba_inst = container_of(fh,
+ struct msm_ba_inst, event_handler);
+
+ return msm_ba_unsubscribe_event((void *)ba_inst, sub);
+}
+
+static int msm_ba_v4l2_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_parm((void *)ba_inst, a);
+}
+
+static int msm_ba_v4l2_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops msm_ba_v4l2_ioctl_ops = {
+ .vidioc_querycap = msm_ba_v4l2_querycap,
+ .vidioc_enum_fmt_vid_cap = msm_ba_v4l2_enum_fmt,
+ .vidioc_enum_fmt_vid_out = msm_ba_v4l2_enum_fmt,
+ .vidioc_s_fmt_vid_cap = msm_ba_v4l2_s_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = msm_ba_v4l2_s_fmt,
+ .vidioc_g_fmt_vid_cap = msm_ba_v4l2_g_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = msm_ba_v4l2_g_fmt,
+ .vidioc_streamon = msm_ba_v4l2_streamon,
+ .vidioc_streamoff = msm_ba_v4l2_streamoff,
+ .vidioc_s_ctrl = msm_ba_v4l2_s_ctrl,
+ .vidioc_g_ctrl = msm_ba_v4l2_g_ctrl,
+ .vidioc_s_ext_ctrls = msm_ba_v4l2_s_ext_ctrl,
+ .vidioc_subscribe_event = msm_ba_v4l2_subscribe_event,
+ .vidioc_unsubscribe_event = msm_ba_v4l2_unsubscribe_event,
+ .vidioc_s_parm = msm_ba_v4l2_s_parm,
+ .vidioc_g_parm = msm_ba_v4l2_g_parm,
+ .vidioc_enum_input = msm_ba_v4l2_enum_input,
+ .vidioc_g_input = msm_ba_v4l2_g_input,
+ .vidioc_s_input = msm_ba_v4l2_s_input,
+ .vidioc_enum_output = msm_ba_v4l2_enum_output,
+ .vidioc_g_output = msm_ba_v4l2_g_output,
+ .vidioc_s_output = msm_ba_v4l2_s_output,
+};
+
+static unsigned int msm_ba_v4l2_poll(struct file *filp,
+ struct poll_table_struct *pt)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(filp, NULL);
+
+ return msm_ba_poll((void *)ba_inst, filp, pt);
+}
+
+void msm_ba_release_video_device(struct video_device *pvdev)
+{
+}
+
+static const struct v4l2_file_operations msm_ba_v4l2_ba_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_ba_v4l2_open,
+ .release = msm_ba_v4l2_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = msm_ba_v4l2_poll,
+};
+
+static int parse_ba_dt(struct platform_device *pdev)
+{
+ uint32_t profile_count = 0;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child_np = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct ba_ctxt *ba_ctxt = msm_ba_get_ba_context();
+ char *key = NULL;
+ uint32_t err = 0, i = 0;
+
+ dev_ctxt = ba_ctxt->dev_ctxt;
+
+ profile_count = of_get_child_count(np);
+ if (profile_count == 0) {
+ dprintk(BA_ERR, "%s: Error reading DT. node=%s",
+ __func__, np->full_name);
+ return -ENODEV;
+ }
+
+ dev_ctxt->msm_ba_inp_cfg = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_ba_input_config) * profile_count,
+ GFP_KERNEL);
+ if (!dev_ctxt->msm_ba_inp_cfg)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_child_of_node(np, child_np) {
+ key = "qcom,type";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].input_type);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,name";
+ err = of_property_read_string(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].name);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-input";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_ip);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-output";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_out);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,sd-name";
+ err = of_property_read_string(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].sd_name);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-node";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_node);
+ if (err)
+ goto read_fail;
+
+
+ key = "qcom,user-type";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].input_user_type);
+ if (err)
+ goto read_fail;
+
+ i++;
+ }
+ dev_ctxt->num_config_inputs = i;
+
+read_fail:
+ if (err) {
+ dprintk(BA_INFO, "%s: Error reading DT. node=%s key=%s",
+ __func__, np->full_name, key);
+ devm_kfree(&pdev->dev, dev_ctxt->msm_ba_inp_cfg);
+
+ dev_ctxt->num_config_inputs = 0;
+ }
+
+ return err;
+}
+
+static int msm_ba_device_init(struct platform_device *pdev,
+ struct msm_ba_dev **ret_dev_ctxt)
+{
+ struct msm_ba_dev *dev_ctxt;
+ int nr = BASE_DEVICE_NUMBER;
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ if ((ret_dev_ctxt == NULL) ||
+ (*ret_dev_ctxt != NULL) ||
+ (pdev == NULL)) {
+ dprintk(BA_ERR, "%s(%d) Invalid params %p %p %p",
+ __func__, __LINE__,
+ ret_dev_ctxt, *ret_dev_ctxt, pdev);
+ return -EINVAL;
+ }
+
+ dev_ctxt = devm_kzalloc(&pdev->dev, sizeof(struct msm_ba_dev),
+ GFP_KERNEL);
+ if (dev_ctxt == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, dev_ctxt);
+
+ INIT_LIST_HEAD(&dev_ctxt->inputs);
+ INIT_LIST_HEAD(&dev_ctxt->instances);
+ INIT_LIST_HEAD(&dev_ctxt->sd_events);
+ INIT_DELAYED_WORK(&dev_ctxt->sd_events_work,
+ msm_ba_subdev_event_hndlr_delayed);
+ mutex_init(&dev_ctxt->dev_cs);
+
+ dev_ctxt->state = BA_DEV_UNINIT;
+
+ strlcpy(dev_ctxt->v4l2_dev.name, MSM_BA_DRV_NAME,
+ sizeof(dev_ctxt->v4l2_dev.name));
+ dev_ctxt->v4l2_dev.dev = &pdev->dev;
+ dev_ctxt->v4l2_dev.notify = msm_ba_subdev_event_hndlr;
+
+ rc = v4l2_device_register(dev_ctxt->v4l2_dev.dev, &dev_ctxt->v4l2_dev);
+ if (!rc) {
+ dev_ctxt->vdev = video_device_alloc();
+ if (dev_ctxt->vdev == NULL) {
+ v4l2_device_unregister(&dev_ctxt->v4l2_dev);
+ rc = -ENOMEM;
+ } else {
+ strlcpy(dev_ctxt->vdev->name,
+ pdev->name, sizeof(dev_ctxt->vdev->name));
+ dev_ctxt->vdev->v4l2_dev = &dev_ctxt->v4l2_dev;
+ dev_ctxt->vdev->release = msm_ba_release_video_device;
+ dev_ctxt->vdev->fops = &msm_ba_v4l2_ba_fops;
+ dev_ctxt->vdev->ioctl_ops = &msm_ba_v4l2_ioctl_ops;
+ dev_ctxt->vdev->minor = nr;
+ dev_ctxt->vdev->vfl_type = VFL_TYPE_GRABBER;
+
+ video_set_drvdata(dev_ctxt->vdev, &dev_ctxt);
+
+ strlcpy(dev_ctxt->mdev.model, MSM_BA_DRV_NAME,
+ sizeof(dev_ctxt->mdev.model));
+ dev_ctxt->mdev.dev = &pdev->dev;
+ rc = media_device_register(&dev_ctxt->mdev);
+ dev_ctxt->v4l2_dev.mdev = &dev_ctxt->mdev;
+ rc = media_entity_init(&dev_ctxt->vdev->entity,
+ 0, NULL, 0);
+ dev_ctxt->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ dev_ctxt->vdev->entity.group_id = 2;
+
+ rc = video_register_device(dev_ctxt->vdev,
+ VFL_TYPE_GRABBER, nr);
+ if (!rc) {
+ dev_ctxt->vdev->entity.name =
+ video_device_node_name(dev_ctxt->vdev);
+ *ret_dev_ctxt = dev_ctxt;
+ } else {
+ dprintk(BA_ERR,
+ "Failed to register BA video device");
+ }
+ }
+ } else {
+ dprintk(BA_ERR, "Failed to register v4l2 device");
+ }
+
+ if (rc) {
+ devm_kfree(&pdev->dev, dev_ctxt);
+ dev_ctxt = NULL;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static int msm_ba_probe(struct platform_device *pdev)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s: pdev %p device id = %d",
+ __func__, pdev, pdev->id);
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt == NULL) {
+ dprintk(BA_ERR, "BA context not yet created");
+ return -EINVAL;
+ }
+ rc = msm_ba_device_init(pdev, &ba_ctxt->dev_ctxt);
+ if (rc)
+ dprintk(BA_ERR, "Failed to init device");
+ else
+ ba_ctxt->dev_ctxt->debugfs_root = msm_ba_debugfs_init_dev(
+ ba_ctxt->dev_ctxt, ba_ctxt->debugfs_root);
+
+ rc = parse_ba_dt(pdev);
+ if (rc < 0) {
+ dprintk(BA_ERR, "%s: devicetree error. Exit init", __func__);
+ return rc;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static int msm_ba_remove(struct platform_device *pdev)
+{
+ struct msm_ba_dev *dev_ctxt = platform_get_drvdata(pdev);
+ struct msm_ba_sd_event *ba_sd_event = NULL;
+ struct msm_ba_sd_event *ba_sd_event_tmp = NULL;
+ int rc = 0;
+
+ if (dev_ctxt == NULL) {
+ dprintk(BA_ERR, "%s invalid device", __func__);
+ rc = -EINVAL;
+ } else {
+ video_unregister_device(dev_ctxt->vdev);
+ v4l2_device_unregister(&dev_ctxt->v4l2_dev);
+ cancel_delayed_work_sync(&dev_ctxt->sd_events_work);
+ list_for_each_entry_safe(ba_sd_event, ba_sd_event_tmp,
+ &dev_ctxt->sd_events, list) {
+ list_del(&ba_sd_event->list);
+ kfree(ba_sd_event);
+ }
+
+ devm_kfree(&pdev->dev, dev_ctxt->msm_ba_inp_cfg);
+ devm_kfree(&pdev->dev, dev_ctxt);
+ dev_ctxt = NULL;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+int msm_ba_create(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt != NULL) {
+ dprintk(BA_ERR, "BA context already created");
+ return -EINVAL;
+ }
+ ba_ctxt = kzalloc(sizeof(struct ba_ctxt), GFP_KERNEL);
+
+ if (ba_ctxt == NULL)
+ return -ENOMEM;
+
+ memset(ba_ctxt, 0x00, sizeof(struct ba_ctxt));
+
+ mutex_init(&ba_ctxt->ba_cs);
+ ba_ctxt->debugfs_root = msm_ba_debugfs_init_drv();
+ if (!ba_ctxt->debugfs_root)
+ dprintk(BA_ERR,
+ "Failed to create debugfs for msm_ba");
+
+ msm_ba_set_ba_context(ba_ctxt);
+
+ dprintk(BA_DBG, "%s(%d), BA create complete",
+ __func__, __LINE__);
+
+ return rc;
+}
+
+int msm_ba_destroy(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt == NULL) {
+ dprintk(BA_ERR, "BA context non existent");
+ return -EINVAL;
+ }
+
+ if (ba_ctxt->dev_ctxt != NULL) {
+ dprintk(BA_ERR, "Device instances exist on BA context");
+ return -EBUSY;
+ }
+ mutex_destroy(&ba_ctxt->ba_cs);
+
+ kfree(ba_ctxt);
+ ba_ctxt = NULL;
+ msm_ba_set_ba_context(ba_ctxt);
+
+ return rc;
+}
+
+static const struct of_device_id msm_ba_dt_match[] = {
+ {.compatible = "qcom,msm-ba"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_ba_dt_match);
+
+static struct platform_driver msm_ba_driver = {
+ .probe = msm_ba_probe,
+ .remove = msm_ba_remove,
+ .driver = {
+ .name = "msm_ba_v4l2",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ba_dt_match,
+ },
+};
+
+static int __init msm_ba_mod_init(void)
+{
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ rc = msm_ba_create();
+ if (!rc) {
+ rc = platform_driver_register(&msm_ba_driver);
+ if (rc) {
+ dprintk(BA_ERR,
+ "Failed to register platform driver");
+ msm_ba_destroy();
+ }
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static void __exit msm_ba_mod_exit(void)
+{
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ platform_driver_unregister(&msm_ba_driver);
+ rc = msm_ba_destroy();
+ dprintk(BA_INFO, "Exit %s", __func__);
+}
+
+module_init(msm_ba_mod_init);
+module_exit(msm_ba_mod_exit);
+
diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c
index 1a11aedc4fe8..9eb5b314ba06 100644
--- a/drivers/watchdog/pcwd_usb.c
+++ b/drivers/watchdog/pcwd_usb.c
@@ -630,6 +630,9 @@ static int usb_pcwd_probe(struct usb_interface *interface,
return -ENODEV;
}
+ if (iface_desc->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
/* check out the endpoint: it has to be Interrupt & IN */
endpoint = &iface_desc->endpoint[0].desc;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index e5733bb537c9..26bbaaefdff4 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -88,12 +88,11 @@ void invalidate_bdev(struct block_device *bdev)
{
struct address_space *mapping = bdev->bd_inode->i_mapping;
- if (mapping->nrpages == 0)
- return;
-
- invalidate_bh_lrus();
- lru_add_drain_all(); /* make sure all lru add caches are flushed */
- invalidate_mapping_pages(mapping, 0, -1);
+ if (mapping->nrpages) {
+ invalidate_bh_lrus();
+ lru_add_drain_all(); /* make sure all lru add caches are flushed */
+ invalidate_mapping_pages(mapping, 0, -1);
+ }
/* 99% of the time, we don't need to flush the cleancache on the bdev.
* But, for the strange corners, lets be cautious
*/
diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 4d8caeb94a11..bdb9c94335f1 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -128,7 +128,7 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (new_mode != old_mode) {
newattrs.ia_mode = new_mode;
newattrs.ia_valid = ATTR_MODE;
- ret = ceph_setattr(dentry, &newattrs);
+ ret = __ceph_setattr(dentry, &newattrs);
if (ret)
goto out_dput;
}
@@ -138,7 +138,7 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (new_mode != old_mode) {
newattrs.ia_mode = old_mode;
newattrs.ia_valid = ATTR_MODE;
- ceph_setattr(dentry, &newattrs);
+ __ceph_setattr(dentry, &newattrs);
}
goto out_dput;
}
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index d98536c8abfc..9f0d99094cc1 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1773,7 +1773,7 @@ static const struct inode_operations ceph_symlink_iops = {
/*
* setattr
*/
-int ceph_setattr(struct dentry *dentry, struct iattr *attr)
+int __ceph_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = d_inode(dentry);
struct ceph_inode_info *ci = ceph_inode(inode);
@@ -1975,11 +1975,6 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
if (inode_dirty_flags)
__mark_inode_dirty(inode, inode_dirty_flags);
- if (ia_valid & ATTR_MODE) {
- err = posix_acl_chmod(inode, attr->ia_mode);
- if (err)
- goto out_put;
- }
if (mask) {
req->r_inode = inode;
@@ -1993,13 +1988,23 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
ceph_cap_string(dirtied), mask);
ceph_mdsc_put_request(req);
- if (mask & CEPH_SETATTR_SIZE)
- __ceph_do_pending_vmtruncate(inode);
ceph_free_cap_flush(prealloc_cf);
+
+ if (err >= 0 && (mask & CEPH_SETATTR_SIZE))
+ __ceph_do_pending_vmtruncate(inode);
+
return err;
-out_put:
- ceph_mdsc_put_request(req);
- ceph_free_cap_flush(prealloc_cf);
+}
+
+int ceph_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ int err;
+
+ err = __ceph_setattr(dentry, attr);
+
+ if (err >= 0 && (attr->ia_valid & ATTR_MODE))
+ err = posix_acl_chmod(d_inode(dentry), attr->ia_mode);
+
return err;
}
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 75b7d125ce66..8c8cb8fe3d32 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -788,6 +788,7 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
return __ceph_do_getattr(inode, NULL, mask, force);
}
extern int ceph_permission(struct inode *inode, int mask);
+extern int __ceph_setattr(struct dentry *dentry, struct iattr *attr);
extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index 819163d8313b..b24275ef97f7 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -369,6 +369,7 @@ static int __set_xattr(struct ceph_inode_info *ci,
if (update_xattr) {
int err = 0;
+
if (xattr && (flags & XATTR_CREATE))
err = -EEXIST;
else if (!xattr && (flags & XATTR_REPLACE))
@@ -376,12 +377,14 @@ static int __set_xattr(struct ceph_inode_info *ci,
if (err) {
kfree(name);
kfree(val);
+ kfree(*newxattr);
return err;
}
if (update_xattr < 0) {
if (xattr)
__remove_xattr(ci, xattr);
kfree(name);
+ kfree(*newxattr);
return 0;
}
}
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index 02b071bf3732..a0b3e7d1be48 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -83,6 +83,9 @@ convert_sfm_char(const __u16 src_char, char *target)
case SFM_COLON:
*target = ':';
break;
+ case SFM_DOUBLEQUOTE:
+ *target = '"';
+ break;
case SFM_ASTERISK:
*target = '*';
break;
@@ -418,6 +421,9 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
case ':':
dest_char = cpu_to_le16(SFM_COLON);
break;
+ case '"':
+ dest_char = cpu_to_le16(SFM_DOUBLEQUOTE);
+ break;
case '*':
dest_char = cpu_to_le16(SFM_ASTERISK);
break;
diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h
index 479bc0a941f3..07ade707fa60 100644
--- a/fs/cifs/cifs_unicode.h
+++ b/fs/cifs/cifs_unicode.h
@@ -57,6 +57,7 @@
* not conflict (although almost does) with the mapping above.
*/
+#define SFM_DOUBLEQUOTE ((__u16) 0xF020)
#define SFM_ASTERISK ((__u16) 0xF021)
#define SFM_QUESTION ((__u16) 0xF025)
#define SFM_COLON ((__u16) 0xF022)
@@ -64,8 +65,8 @@
#define SFM_LESSTHAN ((__u16) 0xF023)
#define SFM_PIPE ((__u16) 0xF027)
#define SFM_SLASH ((__u16) 0xF026)
-#define SFM_PERIOD ((__u16) 0xF028)
-#define SFM_SPACE ((__u16) 0xF029)
+#define SFM_SPACE ((__u16) 0xF028)
+#define SFM_PERIOD ((__u16) 0xF029)
/*
* Mapping mechanism to use when one of the seven reserved characters is
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 5e2f8b8ca08a..b60150e5b5ce 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -717,6 +717,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
if (rc)
return rc;
+ if (server->capabilities & CAP_UNICODE)
+ smb->hdr.Flags2 |= SMBFLG2_UNICODE;
+
/* set up echo request */
smb->hdr.Tid = 0xffff;
smb->hdr.WordCount = 1;
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 35cf990f87d3..a8f5b31636dc 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -272,6 +272,8 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
rc = -EOPNOTSUPP;
break;
case CIFS_IOC_GET_MNT_INFO:
+ if (pSMBFile == NULL)
+ break;
tcon = tlink_tcon(pSMBFile->tlink);
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
break;
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 6cb2603f8a5c..f4afa3b1cc56 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -564,8 +564,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
}
if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
- cifs_dbg(VFS, "invalid size of protocol negotiate response\n");
- return -EIO;
+ cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n",
+ rsplen);
+
+ /* relax check since Mac returns max bufsize allowed on ioctl */
+ if (rsplen > CIFSMaxBufSize)
+ return -EIO;
}
/* check validate negotiate info response matches what we got earlier */
@@ -1518,8 +1522,12 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
* than one credit. Windows typically sets this smaller, but for some
* ioctls it may be useful to allow server to send more. No point
* limiting what the server can send as long as fits in one credit
+ * Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE
+ * (by default, note that it can be overridden to make max larger)
+ * in responses (except for read responses which can be bigger.
+ * We may want to bump this limit up
*/
- req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */
+ req->MaxOutputResponse = cpu_to_le32(CIFSMaxBufSize);
if (is_fsctl)
req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
index e14b1b8fceb0..b9f838af5a72 100644
--- a/fs/ext4/crypto.c
+++ b/fs/ext4/crypto.c
@@ -271,7 +271,7 @@ static int ext4_page_crypto(struct inode *inode,
struct crypto_ablkcipher *tfm = ci->ci_ctfm;
int res = 0;
- req = ablkcipher_request_alloc(tfm, GFP_NOFS);
+ req = ablkcipher_request_alloc(tfm, gfp_flags);
if (!req) {
printk_ratelimited(KERN_ERR
"%s: crypto_request_alloc() failed\n",
diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c
index e2645ca9b95e..026716bdbbfc 100644
--- a/fs/ext4/crypto_fname.c
+++ b/fs/ext4/crypto_fname.c
@@ -344,7 +344,7 @@ int _ext4_fname_disk_to_usr(struct inode *inode,
memcpy(buf+4, &hinfo->minor_hash, 4);
} else
memset(buf, 0, 8);
- memcpy(buf + 8, iname->name + iname->len - 16, 16);
+ memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
oname->name[0] = '_';
ret = digest_encode(buf, 24, oname->name+1);
oname->len = ret + 1;
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
index dd561f916f0b..e4f4fc4e56ab 100644
--- a/fs/ext4/crypto_policy.c
+++ b/fs/ext4/crypto_policy.c
@@ -148,26 +148,38 @@ int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy)
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
struct inode *child)
{
- struct ext4_crypt_info *parent_ci, *child_ci;
+ const struct ext4_crypt_info *parent_ci, *child_ci;
+ struct ext4_encryption_context parent_ctx, child_ctx;
int res;
- if ((parent == NULL) || (child == NULL)) {
- pr_err("parent %p child %p\n", parent, child);
- 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 */
+ /* No restrictions if the parent directory is unencrypted */
if (!ext4_encrypted_inode(parent))
return 1;
- /* if the child directory is not encrypted, this is always a problem */
+
+ /* Encrypted directories must not contain unencrypted files */
if (!ext4_encrypted_inode(child))
return 0;
+
+ /*
+ * Both parent and child are encrypted, so verify they use the same
+ * encryption policy. Compare the fscrypt_info structs if the keys are
+ * available, otherwise retrieve and compare the fscrypt_contexts.
+ *
+ * Note that the fscrypt_context retrieval will be required frequently
+ * when accessing an encrypted directory tree without the key.
+ * Performance-wise this is not a big deal because we already don't
+ * really optimize for file access without the key (to the extent that
+ * such access is even possible), given that any attempted access
+ * already causes a fscrypt_context retrieval and keyring search.
+ *
+ * In any case, if an unexpected error occurs, fall back to "forbidden".
+ */
+
res = ext4_get_encryption_info(parent);
if (res)
return 0;
@@ -176,17 +188,35 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent,
return 0;
parent_ci = EXT4_I(parent)->i_crypt_info;
child_ci = EXT4_I(child)->i_crypt_info;
- if (!parent_ci && !child_ci)
- return 1;
- if (!parent_ci || !child_ci)
+ if (parent_ci && child_ci) {
+ return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key,
+ EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
+ (parent_ci->ci_filename_mode ==
+ child_ci->ci_filename_mode) &&
+ (parent_ci->ci_flags == child_ci->ci_flags);
+ }
+
+ res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &parent_ctx, sizeof(parent_ctx));
+ if (res != sizeof(parent_ctx))
+ return 0;
+
+ res = ext4_xattr_get(child, EXT4_XATTR_INDEX_ENCRYPTION,
+ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &child_ctx, sizeof(child_ctx));
+ if (res != sizeof(child_ctx))
return 0;
- return (memcmp(parent_ci->ci_master_key,
- child_ci->ci_master_key,
- EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
- (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
- (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
- (parent_ci->ci_flags == child_ci->ci_flags));
+ return memcmp(parent_ctx.master_key_descriptor,
+ child_ctx.master_key_descriptor,
+ EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ctx.contents_encryption_mode ==
+ child_ctx.contents_encryption_mode) &&
+ (parent_ctx.filenames_encryption_mode ==
+ child_ctx.filenames_encryption_mode) &&
+ (parent_ctx.flags == child_ctx.flags);
}
/**
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 4caa0c1f77d8..2892a799f6f8 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -5461,6 +5461,11 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
file_update_time(vma->vm_file);
down_read(&EXT4_I(inode)->i_mmap_sem);
+
+ ret = ext4_convert_inline_data(inode);
+ if (ret)
+ goto out_ret;
+
/* Delalloc case is easy... */
if (test_opt(inode->i_sb, DELALLOC) &&
!ext4_should_journal_data(inode) &&
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index b936da9d3d0c..8a196e9b0bf3 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1243,9 +1243,9 @@ static inline int ext4_match(struct ext4_filename *fname,
if (unlikely(!name)) {
if (fname->usr_fname->name[0] == '_') {
int ret;
- if (de->name_len < 16)
+ if (de->name_len <= 32)
return 0;
- ret = memcmp(de->name + de->name_len - 16,
+ ret = memcmp(de->name + ((de->name_len - 17) & ~15),
fname->crypto_buf.name + 8, 16);
return (ret == 0) ? 1 : 0;
}
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 1a6a8ca4de3a..978141e8b800 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -497,7 +497,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
if (IS_ERR(data_page)) {
ret = PTR_ERR(data_page);
- if (ret == ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
+ if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
if (io->io_bio) {
ext4_io_submit(io);
congestion_wait(BLK_RW_ASYNC, HZ/50);
diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c
index ab377d496a39..38349ed5ea51 100644
--- a/fs/f2fs/crypto_fname.c
+++ b/fs/f2fs/crypto_fname.c
@@ -333,7 +333,7 @@ int f2fs_fname_disk_to_usr(struct inode *inode,
memset(buf + 4, 0, 4);
} else
memset(buf, 0, 8);
- memcpy(buf + 8, iname->name + iname->len - 16, 16);
+ memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
oname->name[0] = '_';
ret = digest_encode(buf, 24, oname->name + 1);
oname->len = ret + 1;
diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c
index 5bbd1989d5e6..884f3f0fe29d 100644
--- a/fs/f2fs/crypto_policy.c
+++ b/fs/f2fs/crypto_policy.c
@@ -141,25 +141,38 @@ int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy)
int f2fs_is_child_context_consistent_with_parent(struct inode *parent,
struct inode *child)
{
- struct f2fs_crypt_info *parent_ci, *child_ci;
+ const struct f2fs_crypt_info *parent_ci, *child_ci;
+ struct f2fs_encryption_context parent_ctx, child_ctx;
int res;
- if ((parent == NULL) || (child == NULL)) {
- pr_err("parent %p child %p\n", parent, child);
- 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 */
+ /* No restrictions if the parent directory is unencrypted */
if (!f2fs_encrypted_inode(parent))
return 1;
- /* if the child directory is not encrypted, this is always a problem */
+
+ /* Encrypted directories must not contain unencrypted files */
if (!f2fs_encrypted_inode(child))
return 0;
+
+ /*
+ * Both parent and child are encrypted, so verify they use the same
+ * encryption policy. Compare the fscrypt_info structs if the keys are
+ * available, otherwise retrieve and compare the fscrypt_contexts.
+ *
+ * Note that the fscrypt_context retrieval will be required frequently
+ * when accessing an encrypted directory tree without the key.
+ * Performance-wise this is not a big deal because we already don't
+ * really optimize for file access without the key (to the extent that
+ * such access is even possible), given that any attempted access
+ * already causes a fscrypt_context retrieval and keyring search.
+ *
+ * In any case, if an unexpected error occurs, fall back to "forbidden".
+ */
+
res = f2fs_get_encryption_info(parent);
if (res)
return 0;
@@ -168,17 +181,35 @@ int f2fs_is_child_context_consistent_with_parent(struct inode *parent,
return 0;
parent_ci = F2FS_I(parent)->i_crypt_info;
child_ci = F2FS_I(child)->i_crypt_info;
- if (!parent_ci && !child_ci)
- return 1;
- if (!parent_ci || !child_ci)
+ if (parent_ci && child_ci) {
+ return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key,
+ F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
+ (parent_ci->ci_filename_mode ==
+ child_ci->ci_filename_mode) &&
+ (parent_ci->ci_flags == child_ci->ci_flags);
+ }
+
+ res = f2fs_getxattr(parent, F2FS_XATTR_INDEX_ENCRYPTION,
+ F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &parent_ctx, sizeof(parent_ctx), NULL);
+ if (res != sizeof(parent_ctx))
+ return 0;
+
+ res = f2fs_getxattr(child, F2FS_XATTR_INDEX_ENCRYPTION,
+ F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &child_ctx, sizeof(child_ctx), NULL);
+ if (res != sizeof(child_ctx))
return 0;
- return (memcmp(parent_ci->ci_master_key,
- child_ci->ci_master_key,
- F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
- (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
- (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
- (parent_ci->ci_flags == child_ci->ci_flags));
+ return memcmp(parent_ctx.master_key_descriptor,
+ child_ctx.master_key_descriptor,
+ F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ctx.contents_encryption_mode ==
+ child_ctx.contents_encryption_mode) &&
+ (parent_ctx.filenames_encryption_mode ==
+ child_ctx.filenames_encryption_mode) &&
+ (parent_ctx.flags == child_ctx.flags);
}
/**
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 7c1678ba8f92..60972a559685 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -124,19 +124,29 @@ struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname,
de = &d->dentry[bit_pos];
- /* encrypted case */
+ if (de->hash_code != namehash)
+ goto not_match;
+
de_name.name = d->filename[bit_pos];
de_name.len = le16_to_cpu(de->name_len);
- /* show encrypted name */
- if (fname->hash) {
- if (de->hash_code == fname->hash)
- goto found;
- } else if (de_name.len == name->len &&
- de->hash_code == namehash &&
- !memcmp(de_name.name, name->name, name->len))
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+ if (unlikely(!name->name)) {
+ if (fname->usr_fname->name[0] == '_') {
+ if (de_name.len > 32 &&
+ !memcmp(de_name.name + ((de_name.len - 17) & ~15),
+ fname->crypto_buf.name + 8, 16))
+ goto found;
+ goto not_match;
+ }
+ name->name = fname->crypto_buf.name;
+ name->len = fname->crypto_buf.len;
+ }
+#endif
+ if (de_name.len == name->len &&
+ !memcmp(de_name.name, name->name, name->len))
goto found;
-
+not_match:
if (max_slots && max_len > *max_slots)
*max_slots = max_len;
max_len = 0;
@@ -170,7 +180,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
int max_slots;
f2fs_hash_t namehash;
- namehash = f2fs_dentry_hash(&name);
+ namehash = f2fs_dentry_hash(&name, fname);
f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
@@ -547,7 +557,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
level = 0;
slots = GET_DENTRY_SLOTS(new_name.len);
- dentry_hash = f2fs_dentry_hash(&new_name);
+ dentry_hash = f2fs_dentry_hash(&new_name, NULL);
current_depth = F2FS_I(dir)->i_current_depth;
if (F2FS_I(dir)->chash == dentry_hash) {
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 3c7594b9d109..9dfbfe6dc775 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1723,7 +1723,8 @@ void f2fs_msg(struct super_block *, const char *, const char *, ...);
/*
* hash.c
*/
-f2fs_hash_t f2fs_dentry_hash(const struct qstr *);
+f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
+ struct f2fs_filename *fname);
/*
* node.c
diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c
index 71b7206c431e..b238d2fec3e5 100644
--- a/fs/f2fs/hash.c
+++ b/fs/f2fs/hash.c
@@ -70,7 +70,8 @@ static void str2hashbuf(const unsigned char *msg, size_t len,
*buf++ = pad;
}
-f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
+f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
+ struct f2fs_filename *fname)
{
__u32 hash;
f2fs_hash_t f2fs_hash;
@@ -79,6 +80,10 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
const unsigned char *name = name_info->name;
size_t len = name_info->len;
+ /* encrypted bigname case */
+ if (fname && !fname->disk_name.name)
+ return cpu_to_le32(fname->hash);
+
if (is_dot_dotdot(name_info))
return 0;
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index dbb2cc4df603..f35f3eb3541f 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -321,7 +321,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
if (IS_ERR(ipage))
return NULL;
- namehash = f2fs_dentry_hash(&name);
+ namehash = f2fs_dentry_hash(&name, fname);
inline_dentry = inline_data_addr(ipage);
@@ -486,7 +486,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
f2fs_wait_on_page_writeback(ipage, NODE);
- name_hash = f2fs_dentry_hash(name);
+ name_hash = f2fs_dentry_hash(name, NULL);
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 12935209deca..c3e1cb481fe0 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4041,8 +4041,7 @@ nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_getdeviceinfo *gdev)
{
struct xdr_stream *xdr = &resp->xdr;
- const struct nfsd4_layout_ops *ops =
- nfsd4_layout_ops[gdev->gd_layout_type];
+ const struct nfsd4_layout_ops *ops;
u32 starting_len = xdr->buf->len, needed_len;
__be32 *p;
@@ -4059,6 +4058,7 @@ nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
/* If maxcount is 0 then just update notifications */
if (gdev->gd_maxcount != 0) {
+ ops = nfsd4_layout_ops[gdev->gd_layout_type];
nfserr = ops->encode_getdeviceinfo(xdr, gdev);
if (nfserr) {
/*
@@ -4111,8 +4111,7 @@ nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_layoutget *lgp)
{
struct xdr_stream *xdr = &resp->xdr;
- const struct nfsd4_layout_ops *ops =
- nfsd4_layout_ops[lgp->lg_layout_type];
+ const struct nfsd4_layout_ops *ops;
__be32 *p;
dprintk("%s: err %d\n", __func__, nfserr);
@@ -4135,6 +4134,7 @@ nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
*p++ = cpu_to_be32(lgp->lg_seg.iomode);
*p++ = cpu_to_be32(lgp->lg_layout_type);
+ ops = nfsd4_layout_ops[lgp->lg_layout_type];
nfserr = ops->encode_layoutget(xdr, lgp);
out:
kfree(lgp->lg_content);
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index ff3ffc76a937..3773335791da 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -469,6 +469,7 @@ struct proc_dir_entry *proc_create_mount_point(const char *name)
ent->data = NULL;
ent->proc_fops = NULL;
ent->proc_iops = NULL;
+ parent->nlink++;
if (proc_register(parent, ent) < 0) {
kfree(ent);
parent->nlink--;
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
index 7a19e77fce99..13da7e5245bd 100644
--- a/fs/sdcardfs/dentry.c
+++ b/fs/sdcardfs/dentry.c
@@ -34,6 +34,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
struct dentry *parent_lower_dentry = NULL;
struct dentry *lower_cur_parent_dentry = NULL;
struct dentry *lower_dentry = NULL;
+ struct inode *inode;
+ struct sdcardfs_inode_data *data;
if (flags & LOOKUP_RCU)
return -ECHILD;
@@ -103,6 +105,21 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
spin_unlock(&dentry->d_lock);
spin_unlock(&lower_dentry->d_lock);
}
+ if (!err)
+ goto out;
+
+ /* If our top's inode is gone, we may be out of date */
+ inode = igrab(d_inode(dentry));
+ if (inode) {
+ data = top_data_get(SDCARDFS_I(inode));
+ if (!data || data->abandoned) {
+ d_drop(dentry);
+ err = 0;
+ }
+ if (data)
+ data_put(data);
+ iput(inode);
+ }
out:
dput(parent_dentry);
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index b4595aab5713..85a60fb5ff39 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -26,28 +26,28 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
struct sdcardfs_inode_info *pi = SDCARDFS_I(parent);
struct sdcardfs_inode_info *ci = SDCARDFS_I(child);
- ci->perm = PERM_INHERIT;
- ci->userid = pi->userid;
- ci->d_uid = pi->d_uid;
- ci->under_android = pi->under_android;
- ci->under_cache = pi->under_cache;
- ci->under_obb = pi->under_obb;
- set_top(ci, pi->top);
+ ci->data->perm = PERM_INHERIT;
+ ci->data->userid = pi->data->userid;
+ ci->data->d_uid = pi->data->d_uid;
+ ci->data->under_android = pi->data->under_android;
+ ci->data->under_cache = pi->data->under_cache;
+ ci->data->under_obb = pi->data->under_obb;
+ set_top(ci, pi->top_data);
}
/* 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 sdcardfs_inode_data *top)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
- info->perm = perm;
- info->userid = userid;
- info->d_uid = uid;
- info->under_android = under_android;
- info->under_cache = false;
- info->under_obb = false;
+ info->data->perm = perm;
+ info->data->userid = userid;
+ info->data->d_uid = uid;
+ info->data->under_android = under_android;
+ info->data->under_cache = false;
+ info->data->under_obb = false;
set_top(info, top);
}
@@ -58,7 +58,8 @@ 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_data *parent_data =
+ SDCARDFS_I(d_inode(parent))->data;
appid_t appid;
unsigned long user_num;
int err;
@@ -82,60 +83,61 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
if (!S_ISDIR(d_inode(dentry)->i_mode))
return;
/* Derive custom permissions based on parent and current node */
- switch (parent_info->perm) {
+ switch (parent_data->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->data->perm = PERM_ROOT;
err = kstrtoul(name->name, 10, &user_num);
if (err)
- info->userid = 0;
+ info->data->userid = 0;
else
- info->userid = user_num;
- set_top(info, &info->vfs_inode);
+ info->data->userid = user_num;
+ set_top(info, info->data);
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);
+ info->data->perm = PERM_ANDROID;
+ info->data->under_android = true;
+ set_top(info, info->data);
}
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);
+ info->data->perm = PERM_ANDROID_DATA;
+ set_top(info, info->data);
} 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);
+ info->data->perm = PERM_ANDROID_OBB;
+ info->data->under_obb = true;
+ set_top(info, info->data);
/* 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);
+ info->data->perm = PERM_ANDROID_MEDIA;
+ set_top(info, info->data);
}
break;
case PERM_ANDROID_OBB:
case PERM_ANDROID_DATA:
case PERM_ANDROID_MEDIA:
- info->perm = PERM_ANDROID_PACKAGE;
+ info->data->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);
+ if (appid != 0 && !is_excluded(name->name, parent_data->userid))
+ info->data->d_uid =
+ multiuser_get_uid(parent_data->userid, appid);
+ set_top(info, info->data);
break;
case PERM_ANDROID_PACKAGE:
if (qstr_case_eq(name, &q_cache)) {
- info->perm = PERM_ANDROID_PACKAGE_CACHE;
- info->under_cache = true;
+ info->data->perm = PERM_ANDROID_PACKAGE_CACHE;
+ info->data->under_cache = true;
}
break;
}
@@ -166,7 +168,8 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
struct inode *delegated_inode = NULL;
int error;
struct sdcardfs_inode_info *info;
- struct sdcardfs_inode_info *info_top;
+ struct sdcardfs_inode_data *info_d;
+ struct sdcardfs_inode_data *info_top;
perm_t perm;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
uid_t uid = sbi->options.fs_low_uid;
@@ -174,15 +177,16 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
struct iattr newattrs;
info = SDCARDFS_I(d_inode(dentry));
- perm = info->perm;
- if (info->under_obb) {
+ info_d = info->data;
+ perm = info_d->perm;
+ if (info_d->under_obb) {
perm = PERM_ANDROID_OBB;
- } else if (info->under_cache) {
+ } else if (info_d->under_cache) {
perm = PERM_ANDROID_PACKAGE_CACHE;
} else if (perm == PERM_INHERIT) {
- info_top = SDCARDFS_I(grab_top(info));
+ info_top = top_data_get(info);
perm = info_top->perm;
- release_top(info);
+ data_put(info_top);
}
switch (perm) {
@@ -192,7 +196,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
case PERM_ANDROID_MEDIA:
case PERM_ANDROID_PACKAGE:
case PERM_ANDROID_PACKAGE_CACHE:
- uid = multiuser_get_uid(info->userid, uid);
+ uid = multiuser_get_uid(info_d->userid, uid);
break;
case PERM_ANDROID_OBB:
uid = AID_MEDIA_OBB;
@@ -207,24 +211,24 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
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);
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
else
- gid = multiuser_get_uid(info->userid, get_type(name));
+ gid = multiuser_get_uid(info_d->userid, get_type(name));
break;
case PERM_ANDROID_OBB:
gid = AID_MEDIA_OBB;
break;
case PERM_ANDROID_PACKAGE:
- if (uid_is_app(info->d_uid))
- gid = multiuser_get_ext_gid(info->d_uid);
+ if (uid_is_app(info_d->d_uid))
+ gid = multiuser_get_ext_gid(info_d->d_uid);
else
- gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
break;
case PERM_ANDROID_PACKAGE_CACHE:
- if (uid_is_app(info->d_uid))
- gid = multiuser_get_ext_cache_gid(info->d_uid);
+ if (uid_is_app(info_d->d_uid))
+ gid = multiuser_get_ext_cache_gid(info_d->d_uid);
else
- gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
break;
case PERM_PRE_ROOT:
default:
@@ -257,11 +261,13 @@ retry_deleg:
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_data *data,
+ 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)
+ if (data->perm == PERM_ROOT)
+ return (limit->flags & BY_USERID) ?
+ data->userid == limit->userid : 1;
+ if (data->perm == PERM_PRE_ROOT || data->perm == PERM_ANDROID)
return 1;
return 0;
}
@@ -292,7 +298,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *
}
info = SDCARDFS_I(d_inode(dentry));
- if (needs_fixup(info->perm)) {
+ if (needs_fixup(info->data->perm)) {
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
spin_lock_nested(&child->d_lock, depth + 1);
if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) {
@@ -305,7 +311,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *
}
spin_unlock(&child->d_lock);
}
- } else if (descendant_may_need_fixup(info, limit)) {
+ } else if (descendant_may_need_fixup(info->data, limit)) {
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
__fixup_perms_recursive(child, limit, depth + 1);
}
@@ -349,12 +355,12 @@ int need_graft_path(struct dentry *dentry)
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->data->perm == PERM_ANDROID &&
qstr_case_eq(&dentry->d_name, &obb)) {
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
if (!(sbi->options.multiuser == false
- && parent_info->userid == 0)) {
+ && parent_info->data->userid == 0)) {
ret = 1;
}
}
@@ -415,11 +421,11 @@ int is_base_obbpath(struct dentry *dentry)
spin_lock(&SDCARDFS_D(dentry)->lock);
if (sbi->options.multiuser) {
- if (parent_info->perm == PERM_PRE_ROOT &&
+ if (parent_info->data->perm == PERM_PRE_ROOT &&
qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
- } else if (parent_info->perm == PERM_ANDROID &&
+ } else if (parent_info->data->perm == PERM_ANDROID &&
qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 4f09eebd7d95..60fea424835f 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -23,7 +23,8 @@
#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_data *data)
{
struct cred *cred;
const struct cred *old_cred;
@@ -33,10 +34,10 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_
if (!cred)
return NULL;
- if (info->under_obb)
+ if (data->under_obb)
uid = AID_MEDIA_OBB;
else
- uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid);
+ uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
cred->fsuid = make_kuid(&init_user_ns, uid);
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
@@ -96,7 +97,8 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
if (err)
goto out;
- err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid);
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path,
+ SDCARDFS_I(dir)->data->userid);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
@@ -267,7 +269,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
struct path lower_path;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
- struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
+ struct sdcardfs_inode_data *pd = SDCARDFS_I(dir)->data;
int touch_err = 0;
struct fs_struct *saved_fs;
struct fs_struct *copied_fs;
@@ -336,7 +338,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
make_nomedia_in_obb = 1;
}
- err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pd->userid);
if (err) {
unlock_dir(lower_parent_dentry);
goto out;
@@ -349,12 +351,13 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
fixup_lower_ownership(dentry, dentry->d_name.name);
unlock_dir(lower_parent_dentry);
if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb))
- && (pi->perm == PERM_ANDROID) && (pi->userid == 0))
+ && (pd->perm == PERM_ANDROID) && (pd->userid == 0))
make_nomedia_in_obb = 1;
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
if (make_nomedia_in_obb ||
- ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) {
+ ((pd->perm == PERM_ANDROID)
+ && (qstr_case_eq(&dentry->d_name, &q_data)))) {
REVERT_CRED(saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry)));
set_fs_pwd(current->fs, &lower_path);
@@ -616,7 +619,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma
{
int err;
struct inode tmp;
- struct inode *top = grab_top(SDCARDFS_I(inode));
+ struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode));
if (!top)
return -EINVAL;
@@ -633,10 +636,11 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma
* locks must be dealt with to avoid undefined behavior.
*/
copy_attrs(&tmp, inode);
- tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
- tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
- tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
- release_top(SDCARDFS_I(inode));
+ tmp.i_uid = make_kuid(&init_user_ns, top->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top));
+ tmp.i_mode = (inode->i_mode & S_IFMT)
+ | get_mode(mnt, SDCARDFS_I(inode), top);
+ data_put(top);
tmp.i_sb = inode->i_sb;
if (IS_POSIXACL(inode))
pr_warn("%s: This may be undefined behavior...\n", __func__);
@@ -687,11 +691,11 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
struct iattr lower_ia;
struct dentry *parent;
struct inode tmp;
- struct inode *top;
+ struct sdcardfs_inode_data *top;
const struct cred *saved_cred = NULL;
inode = d_inode(dentry);
- top = grab_top(SDCARDFS_I(inode));
+ top = top_data_get(SDCARDFS_I(inode));
if (!top)
return -EINVAL;
@@ -709,11 +713,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
*
*/
copy_attrs(&tmp, inode);
- tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
- tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
- tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ tmp.i_uid = make_kuid(&init_user_ns, top->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top));
+ tmp.i_mode = (inode->i_mode & S_IFMT)
+ | get_mode(mnt, SDCARDFS_I(inode), top);
tmp.i_size = i_size_read(inode);
- release_top(SDCARDFS_I(inode));
+ data_put(top);
tmp.i_sb = inode->i_sb;
/*
@@ -815,17 +820,17 @@ 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);
+ struct sdcardfs_inode_data *top = top_data_get(info);
if (!top)
return -EINVAL;
stat->dev = inode->i_sb->s_dev;
stat->ino = inode->i_ino;
- stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top);
stat->nlink = inode->i_nlink;
- stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
- stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
+ stat->uid = make_kuid(&init_user_ns, top->d_uid);
+ stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top));
stat->rdev = inode->i_rdev;
stat->size = i_size_read(inode);
stat->atime = inode->i_atime;
@@ -833,7 +838,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt,
stat->ctime = inode->i_ctime;
stat->blksize = (1 << inode->i_blkbits);
stat->blocks = inode->i_blocks;
- release_top(info);
+ data_put(top);
return 0;
}
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index 509d5fbcb472..00ae21151f52 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -71,7 +71,7 @@ struct inode_data {
static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/)
{
struct inode *current_lower_inode = sdcardfs_lower_inode(inode);
- userid_t current_userid = SDCARDFS_I(inode)->userid;
+ userid_t current_userid = SDCARDFS_I(inode)->data->userid;
if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode &&
current_userid == ((struct inode_data *)candidate_data)->id)
@@ -438,7 +438,8 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
goto out;
}
- ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid);
+ ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path,
+ SDCARDFS_I(dir)->data->userid);
if (IS_ERR(ret))
goto out;
if (ret)
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 953d2156d2e9..3c5b51d49d21 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -327,13 +327,13 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
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));
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, SDCARDFS_I(d_inode(sb->s_root))->data);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
} 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));
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, SDCARDFS_I(d_inode(sb->s_root))->data);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
fixup_tmp_permissions(d_inode(sb->s_root));
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index 89196e31073e..8495474258d5 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -156,7 +156,7 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na
struct qstr q_android_secure = QSTR_LITERAL("android_secure");
/* Always block security-sensitive files at root */
- if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) {
+ if (parent_node && SDCARDFS_I(parent_node)->data->perm == PERM_ROOT) {
if (qstr_case_eq(name, &q_autorun)
|| qstr_case_eq(name, &q__android_secure)
|| qstr_case_eq(name, &q_android_secure)) {
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index 2b67b9a8ef9f..31d37d7da4f9 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -30,6 +30,7 @@
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/aio.h>
+#include <linux/kref.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <linux/namei.h>
@@ -81,7 +82,8 @@
*/
#define fixup_tmp_permissions(x) \
do { \
- (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
+ (x)->i_uid = make_kuid(&init_user_ns, \
+ SDCARDFS_I(x)->data->d_uid); \
(x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \
(x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
} while (0)
@@ -97,16 +99,16 @@
*/
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
do { \
- saved_cred = override_fsids(sdcardfs_sbi, info); \
- if (!saved_cred) \
- return -ENOMEM; \
+ saved_cred = override_fsids(sdcardfs_sbi, info->data); \
+ 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); \
+ saved_cred = override_fsids(sdcardfs_sbi, info->data); \
+ if (!saved_cred) \
+ return ERR_PTR(-ENOMEM); \
} while (0)
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
@@ -142,9 +144,11 @@ typedef enum {
struct sdcardfs_sb_info;
struct sdcardfs_mount_options;
struct sdcardfs_inode_info;
+struct sdcardfs_inode_data;
/* 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_data *data);
/* Do not directly use this function, use REVERT_CRED() instead. */
void revert_fsids(const struct cred *old_cred);
@@ -178,18 +182,26 @@ struct sdcardfs_file_info {
const struct vm_operations_struct *lower_vm_ops;
};
-/* sdcardfs inode data in memory */
-struct sdcardfs_inode_info {
- struct inode *lower_inode;
- /* state derived based on current position in hierachy */
+struct sdcardfs_inode_data {
+ struct kref refcount;
+ bool abandoned;
+
perm_t perm;
userid_t userid;
uid_t d_uid;
bool under_android;
bool under_cache;
bool under_obb;
+};
+
+/* sdcardfs inode data in memory */
+struct sdcardfs_inode_info {
+ struct inode *lower_inode;
+ /* state derived based on current position in hierarchy */
+ struct sdcardfs_inode_data *data;
+
/* top folder for ownership */
- struct inode *top;
+ struct sdcardfs_inode_data *top_data;
struct inode vfs_inode;
};
@@ -351,39 +363,56 @@ 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)
+static inline struct sdcardfs_inode_data *data_get(
+ struct sdcardfs_inode_data *data)
{
- struct inode *old_top = NULL;
-
- BUG_ON(IS_ERR_OR_NULL(top));
- if (info->top && info->top != &info->vfs_inode)
- old_top = info->top;
- if (top != &info->vfs_inode)
- igrab(top);
- info->top = top;
- iput(old_top);
+ if (data)
+ kref_get(&data->refcount);
+ return data;
}
-static inline struct inode *grab_top(struct sdcardfs_inode_info *info)
+static inline struct sdcardfs_inode_data *top_data_get(
+ struct sdcardfs_inode_info *info)
{
- struct inode *top = info->top;
+ return data_get(info->top_data);
+}
- if (top)
- return igrab(top);
- else
- return NULL;
+extern void data_release(struct kref *ref);
+
+static inline void data_put(struct sdcardfs_inode_data *data)
+{
+ kref_put(&data->refcount, data_release);
+}
+
+static inline void release_own_data(struct sdcardfs_inode_info *info)
+{
+ /*
+ * This happens exactly once per inode. At this point, the inode that
+ * originally held this data is about to be freed, and all references
+ * to it are held as a top value, and will likely be released soon.
+ */
+ info->data->abandoned = true;
+ data_put(info->data);
}
-static inline void release_top(struct sdcardfs_inode_info *info)
+static inline void set_top(struct sdcardfs_inode_info *info,
+ struct sdcardfs_inode_data *top)
{
- iput(info->top);
+ struct sdcardfs_inode_data *old_top = info->top_data;
+
+ if (top)
+ data_get(top);
+ info->top_data = top;
+ if (old_top)
+ data_put(old_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_data *data)
{
struct sdcardfs_vfsmount_options *opts = mnt->data;
@@ -396,10 +425,12 @@ static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info
*/
return AID_SDCARD_RW;
else
- return multiuser_get_uid(info->userid, opts->gid);
+ return multiuser_get_uid(data->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,
+ struct sdcardfs_inode_data *data)
{
int owner_mode;
int filtered_mode;
@@ -407,12 +438,12 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf
int visible_mode = 0775 & ~opts->mask;
- if (info->perm == PERM_PRE_ROOT) {
+ if (data->perm == PERM_PRE_ROOT) {
/* Top of multi-user view should always be visible to ensure
* secondary users can traverse inside.
*/
visible_mode = 0711;
- } else if (info->under_android) {
+ } else if (data->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.
@@ -481,8 +512,9 @@ struct limit_search {
userid_t userid;
};
-extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
- uid_t uid, bool under_android, struct inode *top);
+extern void setup_derived_state(struct inode *inode, perm_t perm,
+ userid_t userid, uid_t uid, bool under_android,
+ struct sdcardfs_inode_data *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 fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
@@ -601,7 +633,7 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct
{
dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
S_IROTH | S_IXOTH; /* 0775 */
- dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
+ dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->data->d_uid);
dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW);
dest->i_rdev = src->i_rdev;
dest->i_atime = src->i_atime;
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index 8a9c9c7adca2..7f4539b4b249 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -26,6 +26,23 @@
*/
static struct kmem_cache *sdcardfs_inode_cachep;
+/*
+ * To support the top references, we must track some data separately.
+ * An sdcardfs_inode_info always has a reference to its data, and once set up,
+ * also has a reference to its top. The top may be itself, in which case it
+ * holds two references to its data. When top is changed, it takes a ref to the
+ * new data and then drops the ref to the old data.
+ */
+static struct kmem_cache *sdcardfs_inode_data_cachep;
+
+void data_release(struct kref *ref)
+{
+ struct sdcardfs_inode_data *data =
+ container_of(ref, struct sdcardfs_inode_data, refcount);
+
+ kmem_cache_free(sdcardfs_inode_data_cachep, data);
+}
+
/* final actions when unmounting a file system */
static void sdcardfs_put_super(struct super_block *sb)
{
@@ -166,6 +183,7 @@ static void sdcardfs_evict_inode(struct inode *inode)
struct inode *lower_inode;
truncate_inode_pages(&inode->i_data, 0);
+ set_top(SDCARDFS_I(inode), NULL);
clear_inode(inode);
/*
* Decrement a reference to a lower_inode, which was incremented
@@ -173,13 +191,13 @@ static void sdcardfs_evict_inode(struct inode *inode)
*/
lower_inode = sdcardfs_lower_inode(inode);
sdcardfs_set_lower_inode(inode, NULL);
- set_top(SDCARDFS_I(inode), inode);
iput(lower_inode);
}
static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
{
struct sdcardfs_inode_info *i;
+ struct sdcardfs_inode_data *d;
i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL);
if (!i)
@@ -188,6 +206,16 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
/* memset everything up to the inode to 0 */
memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode));
+ d = kmem_cache_alloc(sdcardfs_inode_data_cachep,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!d) {
+ kmem_cache_free(sdcardfs_inode_cachep, i);
+ return NULL;
+ }
+
+ i->data = d;
+ kref_init(&d->refcount);
+
i->vfs_inode.i_version = 1;
return &i->vfs_inode;
}
@@ -196,6 +224,7 @@ static void i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
+ release_own_data(SDCARDFS_I(inode));
kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
}
@@ -214,20 +243,30 @@ static void init_once(void *obj)
int sdcardfs_init_inode_cache(void)
{
- int err = 0;
-
sdcardfs_inode_cachep =
kmem_cache_create("sdcardfs_inode_cache",
sizeof(struct sdcardfs_inode_info), 0,
SLAB_RECLAIM_ACCOUNT, init_once);
+
if (!sdcardfs_inode_cachep)
- err = -ENOMEM;
- return err;
+ return -ENOMEM;
+
+ sdcardfs_inode_data_cachep =
+ kmem_cache_create("sdcardfs_inode_data_cache",
+ sizeof(struct sdcardfs_inode_data), 0,
+ SLAB_RECLAIM_ACCOUNT, NULL);
+ if (!sdcardfs_inode_data_cachep) {
+ kmem_cache_destroy(sdcardfs_inode_cachep);
+ return -ENOMEM;
+ }
+
+ return 0;
}
/* sdcardfs inode cache destructor */
void sdcardfs_destroy_inode_cache(void)
{
+ kmem_cache_destroy(sdcardfs_inode_data_cachep);
kmem_cache_destroy(sdcardfs_inode_cachep);
}
diff --git a/fs/xattr.c b/fs/xattr.c
index 9b932b95d74e..f0da9d24e9ca 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -442,7 +442,7 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
size = XATTR_SIZE_MAX;
kvalue = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (!kvalue) {
- vvalue = vmalloc(size);
+ vvalue = vzalloc(size);
if (!vvalue)
return -ENOMEM;
kvalue = vvalue;
diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h
index 0de6290df4da..6649b6d8c437 100644
--- a/include/drm/drm_mm.h
+++ b/include/drm/drm_mm.h
@@ -37,6 +37,7 @@
* Generic range manager structs
*/
#include <linux/bug.h>
+#include <linux/rbtree.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/spinlock.h>
@@ -48,6 +49,7 @@ enum drm_mm_search_flags {
DRM_MM_SEARCH_DEFAULT = 0,
DRM_MM_SEARCH_BEST = 1 << 0,
DRM_MM_SEARCH_BELOW = 1 << 1,
+ DRM_MM_SEARCH_BOTTOM_UP = 1 << 2,
};
enum drm_mm_allocator_flags {
@@ -61,6 +63,8 @@ enum drm_mm_allocator_flags {
struct drm_mm_node {
struct list_head node_list;
struct list_head hole_stack;
+ struct rb_node rb;
+ struct rb_node hole_node;
unsigned hole_follows : 1;
unsigned scanned_block : 1;
unsigned scanned_prev_free : 1;
@@ -70,6 +74,7 @@ struct drm_mm_node {
unsigned long color;
u64 start;
u64 size;
+ u64 __subtree_last;
struct drm_mm *mm;
};
@@ -79,6 +84,10 @@ struct drm_mm {
/* head_node.node_list is the list of all memory nodes, ordered
* according to the (increasing) start address of the memory node. */
struct drm_mm_node head_node;
+ /* Keep an interval_tree for fast lookup of drm_mm_nodes by address. */
+ struct rb_root interval_tree;
+ struct rb_root holes_tree;
+
unsigned int scan_check_range : 1;
unsigned scan_alignment;
unsigned long scan_color;
@@ -301,6 +310,12 @@ void drm_mm_init(struct drm_mm *mm,
void drm_mm_takedown(struct drm_mm *mm);
bool drm_mm_clean(struct drm_mm *mm);
+struct drm_mm_node *
+drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last);
+
+struct drm_mm_node *
+drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last);
+
void drm_mm_init_scan(struct drm_mm *mm,
u64 size,
unsigned alignment,
diff --git a/include/dt-bindings/regulator/max20010.h b/include/dt-bindings/regulator/max20010.h
new file mode 100644
index 000000000000..492e7287216f
--- /dev/null
+++ b/include/dt-bindings/regulator/max20010.h
@@ -0,0 +1,20 @@
+/* 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 _DT_BINDINGS_REGULATOR_MAX20010_H
+#define _DT_BINDINGS_REGULATOR_MAX20010_H
+
+/* Regulator operating modes */
+#define MAX20010_OPMODE_SYNC 0
+#define MAX20010_OPMODE_FPWM 8
+
+#endif /* _DT_BINDINGS_REGULATOR_MAX20010_H */
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 8f6849084248..e23392517db9 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -330,7 +330,9 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table,
int write, void __user *buffer,
size_t *length, loff_t *ppos);
#endif
-
+extern void wait_for_kprobe_optimizer(void);
+#else
+static inline void wait_for_kprobe_optimizer(void) { }
#endif /* CONFIG_OPTPROBES */
#ifdef CONFIG_KPROBES_ON_FTRACE
extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index d9e12c1b1748..aea4c0f2ef5f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -330,7 +330,9 @@ struct mmc_devfeq_clk_scaling {
atomic_t devfreq_abort;
bool skip_clk_scale_freq_update;
int freq_table_sz;
+ int pltfm_freq_table_sz;
u32 *freq_table;
+ u32 *pltfm_freq_table;
unsigned long total_busy_time_us;
unsigned long target_freq;
unsigned long curr_freq;
@@ -821,6 +823,8 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
}
+void mmc_retune_enable(struct mmc_host *host);
+void mmc_retune_disable(struct mmc_host *host);
void mmc_retune_timer_stop(struct mmc_host *host);
static inline void mmc_retune_needed(struct mmc_host *host)
diff --git a/include/linux/soundwire/soundwire.h b/include/linux/soundwire/soundwire.h
index 2083e7b5da25..1287d2b73bf8 100755
--- a/include/linux/soundwire/soundwire.h
+++ b/include/linux/soundwire/soundwire.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -196,7 +196,6 @@ static inline struct swr_device *to_swr_device(struct device *dev)
* @shutdown: standard shutdown callback used during power down/halt
* @suspend: standard suspend callback used during system suspend
* @resume: standard resume callback used during system resume
- * @startup: additional init operation for slave devices
* @driver: soundwire device drivers should initialize name and
* owner field of this structure
* @id_table: list of soundwire devices supported by this driver
@@ -210,7 +209,6 @@ struct swr_driver {
int (*device_up)(struct swr_device *swr);
int (*device_down)(struct swr_device *swr);
int (*reset_device)(struct swr_device *swr);
- int (*startup)(struct swr_device *swr);
struct device_driver driver;
const struct swr_device_id *id_table;
};
@@ -309,4 +307,6 @@ extern int swr_reset_device(struct swr_device *swr_dev);
extern int swr_slvdev_datapath_control(struct swr_device *swr_dev, u8 dev_num,
bool enable);
extern int swr_remove_from_group(struct swr_device *dev, u8 dev_num);
+
+extern void swr_remove_device(struct swr_device *swr_dev);
#endif /* _LINUX_SOUNDWIRE_H */
diff --git a/include/media/msm_ba.h b/include/media/msm_ba.h
new file mode 100644
index 000000000000..1b51e3f754d8
--- /dev/null
+++ b/include/media/msm_ba.h
@@ -0,0 +1,81 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_BA_H_
+#define _MSM_BA_H_
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <linux/poll.h>
+
+enum msm_ba_ip {
+ BA_IP_CVBS_0 = 0,
+ BA_IP_CVBS_1,
+ BA_IP_CVBS_2,
+ BA_IP_CVBS_3,
+ BA_IP_CVBS_4,
+ BA_IP_CVBS_5,
+ BA_IP_SVIDEO_0,
+ BA_IP_SVIDEO_1,
+ BA_IP_SVIDEO_2,
+ BA_IP_COMPONENT_0,
+ BA_IP_COMPONENT_1,
+ BA_IP_DVI_0,
+ BA_IP_DVI_1,
+ BA_IP_HDMI_1,
+ BA_IP_MHL_1,
+ BA_IP_TTL,
+ BA_IP_MAX = 0xffffffff
+};
+
+enum msm_ba_save_restore_ip {
+ BA_SR_RESTORE_IP = 0,
+ BA_SR_SAVE_IP,
+ BA_SR_MAX = 0xffffffff
+};
+
+struct msm_ba_ext_ops {
+ void (*msm_ba_cb)(void *instance,
+ unsigned int event_id, void *arg);
+};
+
+void *msm_ba_open(const struct msm_ba_ext_ops *ext_ops);
+int msm_ba_close(void *instance);
+int msm_ba_querycap(void *instance, struct v4l2_capability *cap);
+int msm_ba_g_priority(void *instance, enum v4l2_priority *prio);
+int msm_ba_s_priority(void *instance, enum v4l2_priority prio);
+int msm_ba_enum_input(void *instance, struct v4l2_input *input);
+int msm_ba_g_input(void *instance, unsigned int *index);
+int msm_ba_s_input(void *instance, unsigned int index);
+int msm_ba_enum_output(void *instance, struct v4l2_output *output);
+int msm_ba_g_output(void *instance, unsigned int *index);
+int msm_ba_s_output(void *instance, unsigned int index);
+int msm_ba_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
+int msm_ba_s_fmt(void *instance, struct v4l2_format *f);
+int msm_ba_g_fmt(void *instance, struct v4l2_format *f);
+int msm_ba_s_ctrl(void *instance, struct v4l2_control *a);
+int msm_ba_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
+int msm_ba_g_ctrl(void *instance, struct v4l2_control *a);
+int msm_ba_streamon(void *instance, enum v4l2_buf_type i);
+int msm_ba_streamoff(void *instance, enum v4l2_buf_type i);
+int msm_ba_save_restore_input(void *instance, enum msm_ba_save_restore_ip sr);
+int msm_ba_poll(void *instance, struct file *filp,
+ struct poll_table_struct *pt);
+int msm_ba_subscribe_event(void *instance,
+ const struct v4l2_event_subscription *sub);
+int msm_ba_unsubscribe_event(void *instance,
+ const struct v4l2_event_subscription *sub);
+int msm_ba_s_parm(void *instance, struct v4l2_streamparm *a);
+int msm_ba_register_subdev_node(struct v4l2_subdev *sd);
+int msm_ba_unregister_subdev_node(struct v4l2_subdev *sd);
+#endif
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index e4a54cd23211..b63712b00047 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1673,6 +1673,9 @@ struct ieee80211_sta_rates {
* @supp_rates: Bitmap of supported rates (per band)
* @ht_cap: HT capabilities of this STA; restricted to our own capabilities
* @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
+ * @max_rx_aggregation_subframes: maximal amount of frames in a single AMPDU
+ * that this station is allowed to transmit to us.
+ * Can be modified by driver.
* @wme: indicates whether the STA supports QoS/WME (if local devices does,
* otherwise always false)
* @drv_priv: data area for driver use, will always be aligned to
@@ -1699,6 +1702,7 @@ struct ieee80211_sta {
u16 aid;
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
+ u8 max_rx_aggregation_subframes;
bool wme;
u8 uapsd_queues;
u8 max_sp;
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index b434da092b8e..78ca0f4bbd08 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -83,13 +83,6 @@ struct icnss_wlan_enable_cfg {
struct icnss_shadow_reg_cfg *shadow_reg_cfg;
};
-/* MSA Memory Regions Information */
-struct icnss_mem_region_info {
- uint64_t reg_addr;
- uint32_t size;
- uint8_t secure_flag;
-};
-
/* driver modes */
enum icnss_driver_mode {
ICNSS_MISSION,
@@ -152,4 +145,8 @@ extern u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num);
extern int icnss_trigger_recovery(struct device *dev);
extern void cnss_set_cc_source(enum cnss_cc_src cc_source);
extern enum cnss_cc_src cnss_get_cc_source(void);
+extern int icnss_get_driver_load_cnt(void);
+extern void icnss_increment_driver_load_cnt(void);
+extern void icnss_set_cc_source(enum cnss_cc_src cc_source);
+extern enum cnss_cc_src icnss_get_cc_source(void);
#endif /* _ICNSS_WLAN_H_ */
diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h
index 2db61a40e2cc..5eb18cb1a365 100644
--- a/include/soc/qcom/minidump.h
+++ b/include/soc/qcom/minidump.h
@@ -37,12 +37,13 @@ struct md_region {
*/
#ifdef CONFIG_QCOM_MINIDUMP
extern int msm_minidump_add_region(const struct md_region *entry);
+/* Sets to true, if minidump table is initialized */
extern bool minidump_enabled;
#else
static inline int msm_minidump_add_region(const struct md_region *entry)
{
- return -ENODEV;
+ /* Return quietly, if minidump is not supported */
+ return 0;
}
-static inline bool msm_minidump_enabled(void) { return false; }
#endif
#endif
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
index 900d2455993a..e689e9357012 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -65,6 +65,20 @@ struct route_payload {
unsigned int session_id;
};
+struct default_chmixer_param_id_coeff {
+ uint32_t index;
+ uint16_t num_output_channels;
+ uint16_t num_input_channels;
+};
+
+struct msm_pcm_channel_mixer {
+ int output_channel;
+ int input_channels[ADM_MAX_CHANNELS];
+ bool enable;
+ int rule;
+ int channel_weight[ADM_MAX_CHANNELS][ADM_MAX_CHANNELS];
+};
+
int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id,
void *srs_params);
@@ -166,4 +180,8 @@ 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);
+int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id,
+ int session_type,
+ struct msm_pcm_channel_mixer *ch_mixer,
+ int channel_index);
#endif /* __Q6_ADM_V2_H__ */
diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h
index 97069ecabe49..5f9b62c129fc 100644
--- a/include/target/target_core_fabric.h
+++ b/include/target/target_core_fabric.h
@@ -117,7 +117,7 @@ void __transport_register_session(struct se_portal_group *,
struct se_node_acl *, struct se_session *, void *);
void transport_register_session(struct se_portal_group *,
struct se_node_acl *, struct se_session *, void *);
-void target_get_session(struct se_session *);
+int target_get_session(struct se_session *);
void target_put_session(struct se_session *);
ssize_t target_show_dynamic_sessions(struct se_portal_group *, char *);
void transport_free_session(struct se_session *);
@@ -172,8 +172,7 @@ bool target_tpg_has_node_acl(struct se_portal_group *tpg,
const char *);
struct se_node_acl *core_tpg_check_initiator_node_acl(struct se_portal_group *,
unsigned char *);
-int core_tpg_set_initiator_node_queue_depth(struct se_portal_group *,
- unsigned char *, u32, int);
+int core_tpg_set_initiator_node_queue_depth(struct se_node_acl *, u32);
int core_tpg_set_initiator_node_tag(struct se_portal_group *,
struct se_node_acl *, const char *);
int core_tpg_register(struct se_wwn *, struct se_portal_group *, int);
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index 7aa6496c7608..831a0c81cddb 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -102,6 +102,13 @@ struct drm_msm_gem_new {
__u32 handle; /* out */
};
+struct drm_msm_gem_svm_new {
+ __u64 hostptr; /* in, must be page-aligned */
+ __u64 size; /* in, must be page-aligned */
+ __u32 flags; /* in, mask of MSM_BO_x */
+ __u32 handle; /* out */
+};
+
#define MSM_INFO_IOVA 0x01
#define MSM_INFO_FLAGS (MSM_INFO_IOVA)
@@ -218,6 +225,8 @@ struct drm_msm_gem_submit {
__u32 nr_cmds; /* in, number of submit_cmd's */
__u64 __user bos; /* in, ptr to array of submit_bo's */
__u64 __user cmds; /* in, ptr to array of submit_cmd's */
+ __s32 fence_fd; /* gap for the fence_fd which is upstream */
+ __u32 queueid; /* in, submitqueue id */
};
struct drm_msm_gem_submit_profile_buffer {
@@ -346,6 +355,21 @@ struct drm_msm_gem_sync {
__u64 __user ops;
};
+/*
+ * Draw queues allow the user to set specific submission parameter. Command
+ * submissions will specify a specific submit queue id to use. id '0' is
+ * reserved as a "default" drawqueue with medium priority. The user can safely
+ * use and query 0 but cannot destroy it.
+ */
+
+#define MSM_SUBMITQUEUE_FLAGS (0)
+
+struct drm_msm_submitqueue {
+ __u32 flags; /* in, MSM_SUBMITQUEUE_x */
+ __u32 prio; /* in, Priority level */
+ __u32 id; /* out, identifier */
+};
+
#define DRM_MSM_GET_PARAM 0x00
/* placeholder:
#define DRM_MSM_SET_PARAM 0x01
@@ -356,6 +380,10 @@ struct drm_msm_gem_sync {
#define DRM_MSM_GEM_CPU_FINI 0x05
#define DRM_MSM_GEM_SUBMIT 0x06
#define DRM_MSM_WAIT_FENCE 0x07
+/* Gap for upstream DRM_MSM_GEM_MADVISE */
+#define DRM_MSM_GEM_SVM_NEW 0x09
+#define DRM_MSM_SUBMITQUEUE_NEW 0x0A
+#define DRM_MSM_SUBMITQUEUE_CLOSE 0x0B
#define DRM_SDE_WB_CONFIG 0x40
#define DRM_MSM_REGISTER_EVENT 0x41
@@ -395,6 +423,15 @@ struct drm_msm_gem_sync {
struct drm_msm_counter_read)
#define DRM_IOCTL_MSM_GEM_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_GEM_SYNC,\
struct drm_msm_gem_sync)
+#define DRM_IOCTL_MSM_GEM_SVM_NEW \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SVM_NEW, \
+ struct drm_msm_gem_svm_new)
+#define DRM_IOCTL_MSM_SUBMITQUEUE_NEW \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_NEW, \
+ struct drm_msm_submitqueue)
+#define DRM_IOCTL_MSM_SUBMITQUEUE_CLOSE \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_CLOSE, \
+ struct drm_msm_submitqueue)
#if defined(__cplusplus)
}
diff --git a/include/uapi/drm/sde_drm.h b/include/uapi/drm/sde_drm.h
index c7bed3b1ccf3..bef841446247 100644
--- a/include/uapi/drm/sde_drm.h
+++ b/include/uapi/drm/sde_drm.h
@@ -65,6 +65,48 @@
#define SDE_DRM_BITMASK_COUNT 64
/**
+ * Framebuffer modes for "fb_translation_mode" PLANE property
+ *
+ * @SDE_DRM_FB_NON_SEC: IOMMU configuration for this framebuffer mode
+ * is non-secure domain and requires
+ * both stage I and stage II translations when
+ * this buffer is accessed by the display HW.
+ * This is the default mode of all frambuffers.
+ * @SDE_DRM_FB_SEC: IOMMU configuration for this framebuffer mode
+ * is secure domain and requires
+ * both stage I and stage II translations when
+ * this buffer is accessed by the display HW.
+ * @SDE_DRM_FB_NON_SEC_DIR_TRANS: IOMMU configuration for this framebuffer mode
+ * is non-secure domain and requires
+ * only stage II translation when
+ * this buffer is accessed by the display HW.
+ * @SDE_DRM_FB_SEC_DIR_TRANS: IOMMU configuration for this framebuffer mode
+ * is secure domain and requires
+ * only stage II translation when
+ * this buffer is accessed by the display HW.
+*/
+
+#define SDE_DRM_FB_NON_SEC 0
+#define SDE_DRM_FB_SEC 1
+#define SDE_DRM_FB_NON_SEC_DIR_TRANS 2
+#define SDE_DRM_FB_SEC_DIR_TRANS 3
+
+/**
+ * Secure levels for "security_level" CRTC property.
+ * CRTC property which specifies what plane types
+ * can be attached to this CRTC. Plane component
+ * derives the plane type based on the FB_MODE.
+ * @ SDE_DRM_SEC_NON_SEC: Both Secure and non-secure plane types can be
+ * attached to this CRTC. This is the default state of
+ * the CRTC.
+ * @ SDE_DRM_SEC_ONLY: Only secure planes can be added to this CRTC. If a
+ * CRTC is instructed to be in this mode it follows the
+ * platform dependent restrictions.
+ */
+#define SDE_DRM_SEC_NON_SEC 0
+#define SDE_DRM_SEC_ONLY 1
+
+/**
* struct sde_drm_pix_ext_v1 - version 1 of pixel ext structure
* @num_ext_pxls_lr: Number of total horizontal pixels
* @num_ext_pxls_tb: Number of total vertical lines
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 336d318c5187..2e4c02f24a47 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1331,6 +1331,12 @@ enum v4l2_auto_focus_range {
#define V4L2_CID_PAN_SPEED (V4L2_CID_CAMERA_CLASS_BASE+32)
#define V4L2_CID_TILT_SPEED (V4L2_CID_CAMERA_CLASS_BASE+33)
+/* User-class control IDs specific to the msm_ba driver */
+
+#define MSM_BA_PRIV_BASE_START (V4L2_CID_USER_BASE | 0x7000)
+#define MSM_BA_PRIV_SD_NODE_ADDR (MSM_BA_PRIV_BASE_START + 1)
+#define MSM_BA_PRIV_FPS (MSM_BA_PRIV_BASE_START + 2)
+
/* FM Modulator class control IDs */
#define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900)
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 686fc6143010..bb2c4ebf9ff4 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -2187,6 +2187,31 @@ struct v4l2_streamparm {
#define V4L2_EVENT_MSM_VIDC_MAX_CLIENTS (V4L2_EVENT_MSM_VIDC_START + 9)
#define V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED (V4L2_EVENT_MSM_VIDC_START + 10)
+#define V4L2_EVENT_MSM_BA_PRIVATE_EVENT_BASE \
+ (V4L2_EVENT_PRIVATE_START + 0x00005000)
+#define V4L2_EVENT_MSM_BA_START V4L2_EVENT_MSM_BA_PRIVATE_EVENT_BASE
+#define V4L2_EVENT_MSM_BA_DEVICE_AVAILABLE (V4L2_EVENT_MSM_BA_START + 1)
+#define V4L2_EVENT_MSM_BA_DEVICE_UNAVAILABLE \
+ (V4L2_EVENT_MSM_BA_START + 2)
+#define V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED \
+ (V4L2_EVENT_MSM_BA_START + 3)
+#define V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK \
+ (V4L2_EVENT_MSM_BA_START + 4)
+#define V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK \
+ (V4L2_EVENT_MSM_BA_START + 5)
+#define V4L2_EVENT_MSM_BA_SOURCE_CHANGE \
+ (V4L2_EVENT_MSM_BA_START + 6)
+#define V4L2_EVENT_MSM_BA_HDMI_HPD \
+ (V4L2_EVENT_MSM_BA_START + 7)
+#define V4L2_EVENT_MSM_BA_HDMI_CEC_MESSAGE \
+ (V4L2_EVENT_MSM_BA_START + 8)
+#define V4L2_EVENT_MSM_BA_CP \
+ (V4L2_EVENT_MSM_BA_START + 9)
+#define V4L2_EVENT_MSM_BA_CABLE_DETECT \
+ (V4L2_EVENT_MSM_BA_START + 10)
+#define V4L2_EVENT_MSM_BA_ERROR \
+ (V4L2_EVENT_MSM_BA_START + 11)
+
/* Payload for V4L2_EVENT_VSYNC */
struct v4l2_event_vsync {
/* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */
@@ -2442,4 +2467,11 @@ struct v4l2_create_buffers {
#define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */
+/* HDMI rx provide ioctls */
+#define VIDIOC_HDMI_RX_CEC_S_LOGICAL _IOW('V', BASE_VIDIOC_PRIVATE + 0, int)
+#define VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL _IO('V', BASE_VIDIOC_PRIVATE + 1)
+#define VIDIOC_HDMI_RX_CEC_G_PHYSICAL _IOR('V', BASE_VIDIOC_PRIVATE + 2, int)
+#define VIDIOC_HDMI_RX_CEC_G_CONNECTED _IOR('V', BASE_VIDIOC_PRIVATE + 3, int)
+#define VIDIOC_HDMI_RX_CEC_S_ENABLE _IOR('V', BASE_VIDIOC_PRIVATE + 4, int)
+
#endif /* _UAPI__LINUX_VIDEODEV2_H */
diff --git a/kernel/fork.c b/kernel/fork.c
index 622571d2a833..2845c5bdc8e3 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1596,11 +1596,13 @@ static struct task_struct *copy_process(unsigned long clone_flags,
*/
recalc_sigpending();
if (signal_pending(current)) {
- spin_unlock(&current->sighand->siglock);
- write_unlock_irq(&tasklist_lock);
retval = -ERESTARTNOINTR;
goto bad_fork_cancel_cgroup;
}
+ if (unlikely(!(ns_of_pid(pid)->nr_hashed & PIDNS_HASH_ADDING))) {
+ retval = -ENOMEM;
+ goto bad_fork_cancel_cgroup;
+ }
if (likely(p->pid)) {
ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);
@@ -1651,6 +1653,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
return p;
bad_fork_cancel_cgroup:
+ spin_unlock(&current->sighand->siglock);
+ write_unlock_irq(&tasklist_lock);
cgroup_cancel_fork(p, cgrp_ss_priv);
bad_fork_free_pid:
threadgroup_change_end(current);
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 9812d9c0d483..e0449956298e 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -810,8 +810,8 @@ irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
if (!desc)
return;
- __irq_do_set_handler(desc, handle, 1, NULL);
desc->irq_common_data.handler_data = data;
+ __irq_do_set_handler(desc, handle, 1, NULL);
irq_put_desc_busunlock(desc, flags);
}
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index d10ab6b9b5e0..695763516908 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -563,7 +563,7 @@ static void kprobe_optimizer(struct work_struct *work)
}
/* Wait for completing optimization and unoptimization */
-static void wait_for_kprobe_optimizer(void)
+void wait_for_kprobe_optimizer(void)
{
mutex_lock(&kprobe_mutex);
diff --git a/kernel/padata.c b/kernel/padata.c
index 401227e3967c..ecc7b3f452c7 100644
--- a/kernel/padata.c
+++ b/kernel/padata.c
@@ -357,7 +357,7 @@ static int padata_setup_cpumasks(struct parallel_data *pd,
cpumask_and(pd->cpumask.pcpu, pcpumask, cpu_online_mask);
if (!alloc_cpumask_var(&pd->cpumask.cbcpu, GFP_KERNEL)) {
- free_cpumask_var(pd->cpumask.cbcpu);
+ free_cpumask_var(pd->cpumask.pcpu);
return -ENOMEM;
}
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index a65ba137fd15..567ecc826bc8 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -255,7 +255,7 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
* if reparented.
*/
for (;;) {
- set_current_state(TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_INTERRUPTIBLE);
if (pid_ns->nr_hashed == init_pids)
break;
schedule();
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 9abc3e65dbd9..099a1b93bebf 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -3240,7 +3240,8 @@ retry:
sbc_flag |= SBC_FLAG_IDLE_LEAST_LOADED;
}
} else if (stats.best_cpu >= 0) {
- if (stats.best_cpu != task_cpu(p) &&
+ if (stats.best_sibling_cpu >= 0 &&
+ stats.best_cpu != task_cpu(p) &&
stats.min_cost == stats.best_sibling_cpu_cost) {
stats.best_cpu = stats.best_sibling_cpu;
sbc_flag |= SBC_FLAG_BEST_SIBLING;
@@ -5112,6 +5113,26 @@ static void check_enqueue_throttle(struct cfs_rq *cfs_rq)
if (!cfs_bandwidth_used())
return;
+ /* Synchronize hierarchical throttle counter: */
+ if (unlikely(!cfs_rq->throttle_uptodate)) {
+ struct rq *rq = rq_of(cfs_rq);
+ struct cfs_rq *pcfs_rq;
+ struct task_group *tg;
+
+ cfs_rq->throttle_uptodate = 1;
+
+ /* Get closest up-to-date node, because leaves go first: */
+ for (tg = cfs_rq->tg->parent; tg; tg = tg->parent) {
+ pcfs_rq = tg->cfs_rq[cpu_of(rq)];
+ if (pcfs_rq->throttle_uptodate)
+ break;
+ }
+ if (tg) {
+ cfs_rq->throttle_count = pcfs_rq->throttle_count;
+ cfs_rq->throttled_clock_task = rq_clock_task(rq);
+ }
+ }
+
/* an active group must be handled by the update_curr()->put() path */
if (!cfs_rq->runtime_enabled || cfs_rq->curr)
return;
@@ -5502,15 +5523,14 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
/* Don't dequeue parent if it has other entities besides us */
if (cfs_rq->load.weight) {
+ /* Avoid re-evaluating load for this entity: */
+ se = parent_entity(se);
/*
* Bias pick_next to pick a task from this cfs_rq, as
* p is sleeping when it is within its sched_slice.
*/
- if (task_sleep && parent_entity(se))
- set_next_buddy(parent_entity(se));
-
- /* avoid re-evaluating load for this entity */
- se = parent_entity(se);
+ if (task_sleep && se && !throttled_hierarchy(cfs_rq))
+ set_next_buddy(se);
break;
}
flags |= DEQUEUE_SLEEP;
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index a6733b57bcbc..b88f647ea935 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -511,7 +511,7 @@ struct cfs_rq {
u64 throttled_clock, throttled_clock_task;
u64 throttled_clock_task_time;
- int throttled, throttle_count;
+ int throttled, throttle_count, throttle_uptodate;
struct list_head throttled_list;
#endif /* CONFIG_CFS_BANDWIDTH */
#endif /* CONFIG_FAIR_GROUP_SCHED */
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index c9956440d0e6..12ea4ea619ee 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -1471,6 +1471,11 @@ static __init int kprobe_trace_self_tests_init(void)
end:
release_all_trace_kprobes();
+ /*
+ * Wait for the optimizer work to finish. Otherwise it might fiddle
+ * with probes in already freed __init text.
+ */
+ wait_for_kprobe_optimizer();
if (warn)
pr_cont("NG: Some tests are failed. Please check them.\n");
else
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 530e6427f823..47b469663822 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1269,6 +1269,16 @@ out_unlock:
return ret;
}
+/*
+ * FOLL_FORCE can write to even unwritable pmd's, but only
+ * after we've gone through a COW cycle and they are dirty.
+ */
+static inline bool can_follow_write_pmd(pmd_t pmd, unsigned int flags)
+{
+ return pmd_write(pmd) ||
+ ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pmd_dirty(pmd));
+}
+
struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
unsigned long addr,
pmd_t *pmd,
@@ -1279,7 +1289,7 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
assert_spin_locked(pmd_lockptr(mm, pmd));
- if (flags & FOLL_WRITE && !pmd_write(*pmd))
+ if (flags & FOLL_WRITE && !can_follow_write_pmd(*pmd, flags))
goto out;
/* Avoid dumping huge zero page */
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 87fd1a07326b..8402c34592ec 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -1164,7 +1164,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
- if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+ if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE|
+ MSG_CMSG_COMPAT))
return -EINVAL;
if (len < 4 || len > HCI_MAX_FRAME_SIZE)
diff --git a/net/core/dev.c b/net/core/dev.c
index 2587d7f30191..57922df9c250 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4552,6 +4552,19 @@ __sum16 __skb_gro_checksum_complete(struct sk_buff *skb)
}
EXPORT_SYMBOL(__skb_gro_checksum_complete);
+static void net_rps_send_ipi(struct softnet_data *remsd)
+{
+#ifdef CONFIG_RPS
+ while (remsd) {
+ struct softnet_data *next = remsd->rps_ipi_next;
+
+ if (cpu_online(remsd->cpu))
+ smp_call_function_single_async(remsd->cpu, &remsd->csd);
+ remsd = next;
+ }
+#endif
+}
+
/*
* net_rps_action_and_irq_enable sends any pending IPI's for rps.
* Note: called with local irq disabled, but exits with local irq enabled.
@@ -4567,20 +4580,7 @@ static void net_rps_action_and_irq_enable(struct softnet_data *sd)
local_irq_enable();
/* Send pending IPI's to kick RPS processing on remote cpus. */
- while (remsd) {
- struct softnet_data *next = remsd->rps_ipi_next;
-
- if (cpu_online(remsd->cpu)) {
- smp_call_function_single_async(remsd->cpu,
- &remsd->csd);
- } else {
- pr_err("%s() cpu offline\n", __func__);
- rps_lock(remsd);
- remsd->backlog.state = 0;
- rps_unlock(remsd);
- }
- remsd = next;
- }
+ net_rps_send_ipi(remsd);
} else
#endif
local_irq_enable();
@@ -7495,7 +7495,7 @@ static int dev_cpu_callback(struct notifier_block *nfb,
struct sk_buff **list_skb;
struct sk_buff *skb;
unsigned int cpu, oldcpu = (unsigned long)ocpu;
- struct softnet_data *sd, *oldsd;
+ struct softnet_data *sd, *oldsd, *remsd;
if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
return NOTIFY_OK;
@@ -7539,6 +7539,13 @@ static int dev_cpu_callback(struct notifier_block *nfb,
raise_softirq_irqoff(NET_TX_SOFTIRQ);
local_irq_enable();
+#ifdef CONFIG_RPS
+ remsd = oldsd->rps_ipi_list;
+ oldsd->rps_ipi_list = NULL;
+#endif
+ /* send out pending IPI's on offline CPU */
+ net_rps_send_ipi(remsd);
+
/* Process offline CPU's input_pkt_queue */
while ((skb = __skb_dequeue(&oldsd->process_queue))) {
netif_rx_ni(skb);
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 48d0dc89b58d..e735f781e4f3 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -1168,11 +1168,10 @@ static int ipxitf_ioctl(unsigned int cmd, void __user *arg)
sipx->sipx_network = ipxif->if_netnum;
memcpy(sipx->sipx_node, ipxif->if_node,
sizeof(sipx->sipx_node));
- rc = -EFAULT;
+ rc = 0;
if (copy_to_user(arg, &ifr, sizeof(ifr)))
- break;
+ rc = -EFAULT;
ipxitf_put(ipxif);
- rc = 0;
break;
}
case SIOCAIPXITFCRT:
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index f598ff80b30e..a830356b94ac 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -290,10 +290,13 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
buf_size = IEEE80211_MAX_AMPDU_BUF;
/* make sure the size doesn't exceed the maximum supported by the hw */
- if (buf_size > local->hw.max_rx_aggregation_subframes)
- buf_size = local->hw.max_rx_aggregation_subframes;
+ if (buf_size > sta->sta.max_rx_aggregation_subframes)
+ buf_size = sta->sta.max_rx_aggregation_subframes;
params.buf_size = buf_size;
+ ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n",
+ buf_size, sta->sta.addr);
+
/* examine state machine */
mutex_lock(&sta->ampdu_mlme.mtx);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index fe88071d4abb..d2075804cbff 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -330,6 +330,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
memcpy(sta->addr, addr, ETH_ALEN);
memcpy(sta->sta.addr, addr, ETH_ALEN);
+ sta->sta.max_rx_aggregation_subframes =
+ local->hw.max_rx_aggregation_subframes;
+
sta->local = local;
sta->sdata = sdata;
sta->rx_stats.last_rx = jiffies;
diff --git a/net/netfilter/xt_HARDIDLETIMER.c b/net/netfilter/xt_HARDIDLETIMER.c
index 06322e4de632..ecb4a7fb6bf8 100644
--- a/net/netfilter/xt_HARDIDLETIMER.c
+++ b/net/netfilter/xt_HARDIDLETIMER.c
@@ -4,7 +4,7 @@
* Netfilter module to trigger a timer when packet matches.
* After timer expires a kevent will be sent.
*
- * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015, 2017 The Linux Foundation. All rights reserved.
*
* Copyright (C) 2004, 2010 Nokia Corporation
*
@@ -187,6 +187,8 @@ static int hardidletimer_tg_create(struct hardidletimer_tg_info *info)
pr_debug("couldn't add file to sysfs");
goto out_free_attr;
}
+ /* notify userspace */
+ kobject_uevent(hardidletimer_tg_kobj, KOBJ_ADD);
list_add(&info->timer->entry, &hardidletimer_tg_list);
diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c
index 80b32de1d99c..f9eb8641dc3a 100644
--- a/net/netfilter/xt_IDLETIMER.c
+++ b/net/netfilter/xt_IDLETIMER.c
@@ -307,6 +307,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info)
pr_debug("couldn't add file to sysfs");
goto out_free_attr;
}
+ /* notify userspace */
+ kobject_uevent(idletimer_tg_kobj, KOBJ_ADD);
list_add(&info->timer->entry, &idletimer_tg_list);
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index c8c4f547b4f1..9ff010cee67e 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -771,9 +771,6 @@ country KR: DFS-ETSI
# ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99
(57240 - 65880 @ 2160), (43)
-country KP: DFS-ETSI
- (2402 - 2482 @ 40), (20)
-
country KW: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
@@ -1335,9 +1332,6 @@ country SV: DFS-FCC
(5250 - 5330 @ 20), (23), DFS
(5735 - 5835 @ 20), (30)
-country SY:
- (2402 - 2482 @ 40), (20)
-
country TC: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (24), AUTO-BW
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index ed5a9c110b3a..9ce9d5003dcc 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -203,10 +203,11 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
cause = "missing-hash";
status = INTEGRITY_NOLABEL;
- if (opened & FILE_CREATED) {
+ if (opened & FILE_CREATED)
iint->flags |= IMA_NEW_FILE;
+ if ((iint->flags & IMA_NEW_FILE) &&
+ !(iint->flags & IMA_DIGSIG_REQUIRED))
status = INTEGRITY_PASS;
- }
goto out;
}
diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
index 00f2aa766363..ee057e99bc41 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
+++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
@@ -1400,8 +1400,26 @@ static int msm_anlg_cdc_codec_enable_on_demand_supply(
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- if (atomic_inc_return(&supply->ref) == 1)
+ if (atomic_inc_return(&supply->ref) == 1) {
+ ret = regulator_set_voltage(supply->supply,
+ supply->min_uv,
+ supply->max_uv);
+ if (ret) {
+ dev_err(codec->dev,
+ "Setting regulator voltage(en) for micbias with err = %d\n",
+ ret);
+ goto out;
+ }
+ ret = regulator_set_load(supply->supply,
+ supply->optimum_ua);
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "Setting regulator optimum mode(en) failed for micbias with err = %d\n",
+ ret);
+ goto out;
+ }
ret = regulator_enable(supply->supply);
+ }
if (ret)
dev_err(codec->dev, "%s: Failed to enable %s\n",
__func__,
@@ -1413,12 +1431,27 @@ static int msm_anlg_cdc_codec_enable_on_demand_supply(
__func__, on_demand_supply_name[w->shift]);
goto out;
}
- if (atomic_dec_return(&supply->ref) == 0)
+ if (atomic_dec_return(&supply->ref) == 0) {
ret = regulator_disable(supply->supply);
if (ret)
dev_err(codec->dev, "%s: Failed to disable %s\n",
__func__,
on_demand_supply_name[w->shift]);
+ ret = regulator_set_voltage(supply->supply,
+ 0,
+ supply->max_uv);
+ if (ret) {
+ dev_err(codec->dev,
+ "Setting regulator voltage(dis) failed for micbias with err = %d\n",
+ ret);
+ goto out;
+ }
+ ret = regulator_set_load(supply->supply, 0);
+ if (ret < 0)
+ dev_err(codec->dev,
+ "Setting regulator optimum mode(dis) failed for micbias with err = %d\n",
+ ret);
+ }
break;
default:
break;
@@ -3685,6 +3718,30 @@ static struct regulator *msm_anlg_cdc_find_regulator(
return NULL;
}
+static void msm_anlg_cdc_update_micbias_regulator(
+ const struct sdm660_cdc_priv *sdm660_cdc,
+ const char *name,
+ struct on_demand_supply *micbias_supply)
+{
+ int i;
+ struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data;
+
+ for (i = 0; i < sdm660_cdc->num_of_supplies; i++) {
+ if (sdm660_cdc->supplies[i].supply &&
+ !strcmp(sdm660_cdc->supplies[i].supply, name)) {
+ micbias_supply->supply =
+ sdm660_cdc->supplies[i].consumer;
+ micbias_supply->min_uv = pdata->regulator[i].min_uv;
+ micbias_supply->max_uv = pdata->regulator[i].max_uv;
+ micbias_supply->optimum_ua =
+ pdata->regulator[i].optimum_ua;
+ return;
+ }
+ }
+
+ dev_err(sdm660_cdc->dev, "Error: regulator not found:%s\n", name);
+}
+
static int msm_anlg_cdc_device_down(struct snd_soc_codec *codec)
{
struct msm_asoc_mach_data *pdata = NULL;
@@ -4114,10 +4171,10 @@ static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec)
wcd9xxx_spmi_set_codec(codec);
- sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].supply =
- msm_anlg_cdc_find_regulator(
+ msm_anlg_cdc_update_micbias_regulator(
sdm660_cdc,
- on_demand_supply_name[ON_DEMAND_MICBIAS]);
+ on_demand_supply_name[ON_DEMAND_MICBIAS],
+ &sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS]);
atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].ref,
0);
@@ -4183,7 +4240,7 @@ static int msm_anlg_cdc_enable_static_supplies_to_optimum(
if (pdata->regulator[i].ondemand)
continue;
if (regulator_count_voltages(
- sdm660_cdc->supplies[i].consumer) <= 0)
+ sdm660_cdc->supplies[i].consumer) <= 0)
continue;
ret = regulator_set_voltage(
@@ -4216,7 +4273,7 @@ static int msm_anlg_cdc_disable_static_supplies_to_optimum(
if (pdata->regulator[i].ondemand)
continue;
if (regulator_count_voltages(
- sdm660_cdc->supplies[i].consumer) <= 0)
+ sdm660_cdc->supplies[i].consumer) <= 0)
continue;
regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0,
pdata->regulator[i].max_uv);
@@ -4317,6 +4374,28 @@ static int msm_anlg_cdc_init_supplies(struct sdm660_cdc_priv *sdm660_cdc,
if (regulator_count_voltages(
sdm660_cdc->supplies[i].consumer) <= 0)
continue;
+ if (pdata->regulator[i].ondemand) {
+ ret = regulator_set_voltage(
+ sdm660_cdc->supplies[i].consumer,
+ 0, pdata->regulator[i].max_uv);
+ if (ret) {
+ dev_err(sdm660_cdc->dev,
+ "Setting regulator voltage failed for regulator %s err = %d\n",
+ sdm660_cdc->supplies[i].supply, ret);
+ goto err_supplies;
+ }
+ ret = regulator_set_load(
+ sdm660_cdc->supplies[i].consumer, 0);
+ if (ret < 0) {
+ dev_err(sdm660_cdc->dev,
+ "Setting regulator optimum mode failed for regulator %s err = %d\n",
+ sdm660_cdc->supplies[i].supply, ret);
+ goto err_supplies;
+ } else {
+ ret = 0;
+ continue;
+ }
+ }
ret = regulator_set_voltage(sdm660_cdc->supplies[i].consumer,
pdata->regulator[i].min_uv,
pdata->regulator[i].max_uv);
diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
index 0c9e9a6aeb6a..9563565f36d2 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
+++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
@@ -144,6 +144,9 @@ struct sdm660_cdc_regulator {
struct on_demand_supply {
struct regulator *supply;
atomic_t ref;
+ int min_uv;
+ int max_uv;
+ int optimum_ua;
};
struct wcd_imped_i_ref {
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index eaaca97e2b8e..0af656ce48f0 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -1123,54 +1123,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wsa881x = {
.get_regmap = wsa881x_get_regmap,
};
-static int wsa881x_swr_startup(struct swr_device *swr_dev)
-{
- int ret = 0;
- u8 devnum = 0;
- struct wsa881x_priv *wsa881x;
-
- wsa881x = swr_get_dev_data(swr_dev);
- if (!wsa881x) {
- dev_err(&swr_dev->dev, "%s: wsa881x is NULL\n", __func__);
- return -EINVAL;
- }
-
- /*
- * Add 5msec delay to provide sufficient time for
- * soundwire auto enumeration of slave devices as
- * as per HW requirement.
- */
- usleep_range(5000, 5010);
- ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum);
- if (ret) {
- dev_dbg(&swr_dev->dev,
- "%s get devnum %d for dev addr %lx failed\n",
- __func__, devnum, swr_dev->addr);
- goto err;
- }
- swr_dev->dev_num = devnum;
-
- wsa881x->regmap = devm_regmap_init_swr(swr_dev,
- &wsa881x_regmap_config);
- if (IS_ERR(wsa881x->regmap)) {
- ret = PTR_ERR(wsa881x->regmap);
- dev_err(&swr_dev->dev, "%s: regmap_init failed %d\n",
- __func__, ret);
- goto err;
- }
-
- ret = snd_soc_register_codec(&swr_dev->dev, &soc_codec_dev_wsa881x,
- NULL, 0);
- if (ret) {
- dev_err(&swr_dev->dev, "%s: Codec registration failed\n",
- __func__);
- goto err;
- }
-
-err:
- return ret;
-}
-
static int wsa881x_gpio_ctrl(struct wsa881x_priv *wsa881x, bool enable)
{
int ret = 0;
@@ -1232,6 +1184,7 @@ static int wsa881x_swr_probe(struct swr_device *pdev)
{
int ret = 0;
struct wsa881x_priv *wsa881x;
+ u8 devnum = 0;
wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv),
GFP_KERNEL);
@@ -1291,8 +1244,43 @@ static int wsa881x_swr_probe(struct swr_device *pdev)
&codec_debug_ops);
}
}
+
+ /*
+ * Add 5msec delay to provide sufficient time for
+ * soundwire auto enumeration of slave devices as
+ * as per HW requirement.
+ */
+ usleep_range(5000, 5010);
+ ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum);
+ if (ret) {
+ dev_dbg(&pdev->dev,
+ "%s get devnum %d for dev addr %lx failed\n",
+ __func__, devnum, pdev->addr);
+ goto dev_err;
+ }
+ pdev->dev_num = devnum;
+
+ wsa881x->regmap = devm_regmap_init_swr(pdev,
+ &wsa881x_regmap_config);
+ if (IS_ERR(wsa881x->regmap)) {
+ ret = PTR_ERR(wsa881x->regmap);
+ dev_err(&pdev->dev, "%s: regmap_init failed %d\n",
+ __func__, ret);
+ goto dev_err;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wsa881x,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Codec registration failed\n",
+ __func__);
+ goto dev_err;
+ }
+
return 0;
+dev_err:
+ swr_remove_device(pdev);
err:
return ret;
}
@@ -1425,7 +1413,6 @@ static struct swr_driver wsa881x_codec_driver = {
.device_up = wsa881x_swr_up,
.device_down = wsa881x_swr_down,
.reset_device = wsa881x_swr_reset,
- .startup = wsa881x_swr_startup,
};
static int __init wsa881x_codec_init(void)
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
index a71fb74d35bc..6b1c150f1ee4 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -283,6 +283,7 @@ static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
*dai_data->status_mask);
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ memset(&dai_data->ca, 0, sizeof(dai_data->ca));
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index f41c6107aac8..974d4a582540 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -81,6 +81,12 @@ static bool is_custom_stereo_on;
static bool is_ds2_on;
static bool swap_ch;
+#define WEIGHT_0_DB 0x4000
+/* all the FEs which can support channel mixer */
+static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE];
+/* input BE for each FE */
+static int channel_input[MSM_FRONTEND_DAI_MM_SIZE][ADM_MAX_CHANNELS];
+
enum {
MADNONE,
MADAUDIO,
@@ -1257,6 +1263,62 @@ static u32 msm_pcm_routing_get_voc_sessionid(u16 val)
return session_id;
}
+static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
+ int dspst_id, int stream_type)
+{
+ int copp_idx = 0;
+ int sess_type = 0;
+ int i = 0, j = 0, be_id;
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return 0;
+ }
+
+ if (!(channel_mixer[fe_id].enable)) {
+ pr_debug("%s: channel mixer not enabled for FE %d\n",
+ __func__, fe_id);
+ return 0;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+ sess_type = SESSION_TYPE_RX;
+ else
+ sess_type = SESSION_TYPE_TX;
+
+ for (i = 0; i < ADM_MAX_CHANNELS && channel_input[fe_id][i] > 0;
+ ++i) {
+ be_id = channel_input[fe_id][i] - 1;
+ channel_mixer[fe_id].input_channels[i] =
+ msm_bedais[be_id].channel;
+
+ if ((msm_bedais[be_id].active) &&
+ test_bit(fe_id,
+ &msm_bedais[be_id].fe_sessions[0])) {
+ unsigned long copp =
+ session_copp_map[fe_id][sess_type][be_id];
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ if (test_bit(j, &copp)) {
+ copp_idx = j;
+ break;
+ }
+ }
+
+ pr_debug("%s: fe %d, be %d, channel %d, copp %d\n",
+ __func__,
+ fe_id, be_id, msm_bedais[be_id].channel,
+ copp_idx);
+ ret = adm_programable_channel_mixer(
+ msm_bedais[be_id].port_id,
+ copp_idx, dspst_id, sess_type,
+ channel_mixer + fe_id, i);
+ }
+ }
+
+ return ret;
+}
+
int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
int dspst_id, int stream_type)
{
@@ -1265,6 +1327,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
u32 channels, sample_rate;
uint16_t bits_per_sample = 16;
uint32_t passthr_mode = LEGACY_PCM;
+ int ret = 0;
if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
/* bad ID assigned in machine driver */
@@ -1388,8 +1451,11 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
adm_matrix_map(path_type, payload, perf_mode, passthr_mode);
msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode);
}
+
+ ret = msm_pcm_routing_channel_mixer(fedai_id, perf_mode,
+ dspst_id, stream_type);
mutex_unlock(&routing_lock);
- return 0;
+ return ret;
}
int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode,
@@ -2614,6 +2680,649 @@ static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
return 1;
}
+static int msm_pcm_get_channel_rule_index(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = channel_mixer[fe_id].rule;
+
+ return 0;
+}
+
+static int msm_pcm_put_channel_rule_index(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ channel_mixer[fe_id].rule = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int msm_pcm_get_out_chs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] =
+ channel_mixer[fe_id].output_channel;
+ return 0;
+}
+
+static int msm_pcm_put_out_chs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: fe_id is %d, output channels = %d\n", __func__,
+ fe_id,
+ (unsigned int)(ucontrol->value.integer.value[0]));
+ channel_mixer[fe_id].output_channel =
+ (unsigned int)(ucontrol->value.integer.value[0]);
+
+ return 1;
+}
+
+static const char *const ch_mixer[] = {"Disable", "Enable"};
+
+/* If new backend is added, need update this array */
+static const char *const be_name[] = {
+"ZERO", "PRI_I2S_RX", "PRI_I2S_TX", "SLIM_0_RX",
+"SLIM_0_TX", "HDMI_RX", "INT_BT_SCO_RX", "INT_BT_SCO_TX",
+"INT_FM_RX", "INT_FM_TX", "AFE_PCM_RX", "AFE_PCM_TX",
+"AUXPCM_RX", "AUXPCM_TX", "VOICE_PLAYBACK_TX", "VOICE2_PLAYBACK_TX",
+"INCALL_RECORD_RX", "INCALL_RECORD_TX", "MI2S_RX", "MI2S_TX",
+"SEC_I2S_RX", "SLIM_1_RX", "SLIM_1_TX", "SLIM_2_RX",
+"SLIM_2_TX", "SLIM_3_RX", "SLIM_3_TX", "SLIM_4_RX",
+"SLIM_4_TX", "SLIM_5_RX", "SLIM_5_TX", "SLIM_6_RX",
+"SLIM_6_TX", "SLIM_7_RX", "SLIM_7_TX", "SLIM_8_RX",
+"SLIM_8_TX", "EXTPROC_RX", "EXTPROC_TX", "EXPROC_EC_TX",
+"QUAT_MI2S_RX", "QUAT_MI2S_TX", "SECOND_MI2S_RX", "SECOND_MI2S_TX",
+"PRI_MI2S_RX", "PRI_MI2S_TX", "TERT_MI2S_RX", "TERT_MI2S_TX",
+"AUDIO_I2S_RX", "SEC_AUXPCM_RX", "SEC_AUXPCM_TX", "SPDIF_RX",
+"SECOND_MI2S_RX_SD1", "QUIN_MI2S_RX", "QUIN_MI2S_TX", "SENARY_MI2S_TX",
+"PRI_TDM_RX_0", "PRI_TDM_TX_0", "PRI_TDM_RX_1", "PRI_TDM_TX_1",
+"PRI_TDM_RX_2", "PRI_TDM_TX_2", "PRI_TDM_RX_3", "PRI_TDM_TX_3",
+"PRI_TDM_RX_4", "PRI_TDM_TX_4", "PRI_TDM_RX_5", "PRI_TDM_TX_5",
+"PRI_TDM_RX_6", "PRI_TDM_TX_6", "PRI_TDM_RX_7", "PRI_TDM_TX_7",
+"SEC_TDM_RX_0", "SEC_TDM_TX_0", "SEC_TDM_RX_1", "SEC_TDM_TX_1",
+"SEC_TDM_RX_2", "SEC_TDM_TX_2", "SEC_TDM_RX_3", "SEC_TDM_TX_3",
+"SEC_TDM_RX_4", "SEC_TDM_TX_4", "SEC_TDM_RX_5", "SEC_TDM_TX_5",
+"SEC_TDM_RX_6", "SEC_TDM_TX_6", "SEC_TDM_RX_7", "SEC_TDM_TX_7",
+"TERT_TDM_RX_0", "TERT_TDM_TX_0", "TERT_TDM_RX_1", "TERT_TDM_TX_1",
+"TERT_TDM_RX_2", "TERT_TDM_TX_2", "TERT_TDM_RX_3", "TERT_TDM_TX_3",
+"TERT_TDM_RX_4", "TERT_TDM_TX_4", "TERT_TDM_RX_5", "TERT_TDM_TX_5",
+"TERT_TDM_RX_6", "TERT_TDM_TX_6", "TERT_TDM_RX_7", "TERT_TDM_TX_7",
+"QUAT_TDM_RX_0", "QUAT_TDM_TX_0", "QUAT_TDM_RX_1", "QUAT_TDM_TX_1",
+"QUAT_TDM_RX_2", "QUAT_TDM_TX_2", "QUAT_TDM_RX_3", "QUAT_TDM_TX_3",
+"QUAT_TDM_RX_4", "QUAT_TDM_TX_4", "QUAT_TDM_RX_5", "QUAT_TDM_TX_5",
+"QUAT_TDM_RX_6", "QUAT_TDM_TX_6", "QUAT_TDM_RX_7", "QUAT_TDM_TX_7",
+"INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX",
+"TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX", "QUAT_AUXPCM_TX",
+"INT0_MI2S_RX", "INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX",
+"INT2_MI2S_RX", "INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX",
+"INT4_MI2S_RX", "INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX",
+"INT6_MI2S_RX", "INT6_MI2S_TX"
+};
+
+static SOC_ENUM_SINGLE_DECL(mm1_channel_mux,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, ch_mixer);
+static SOC_ENUM_SINGLE_DECL(mm2_channel_mux,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA2, ch_mixer);
+static SOC_ENUM_SINGLE_DECL(mm3_channel_mux,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA3, ch_mixer);
+static SOC_ENUM_SINGLE_DECL(mm4_channel_mux,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA4, ch_mixer);
+
+static SOC_ENUM_DOUBLE_DECL(mm1_ch1_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 0, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch2_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 1, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch3_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 2, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch4_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 3, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch5_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 4, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch6_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 5, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch7_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 6, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch8_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 7, be_name);
+
+static int msm_pcm_get_ctl_enum_info(struct snd_ctl_elem_info *uinfo,
+ unsigned int channels,
+ unsigned int items, const char *const names[])
+{
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+
+ WARN(strlen(names[uinfo->value.enumerated.item]) >=
+ sizeof(uinfo->value.enumerated.name),
+ "ALSA: too long item name '%s'\n",
+ names[uinfo->value.enumerated.item]);
+ strlcpy(uinfo->value.enumerated.name,
+ names[uinfo->value.enumerated.item],
+ sizeof(uinfo->value.enumerated.name));
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ uinfo->value.enumerated.items = ARRAY_SIZE(ch_mixer);
+ msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts);
+
+ return 0;
+}
+static int msm_pcm_channel_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_enum *)
+ kcontrol->private_value)->shift_l;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: FE %d %s\n", __func__,
+ fe_id,
+ channel_mixer[fe_id].enable ? "Enabled" : "Disabled");
+ ucontrol->value.enumerated.item[0] = channel_mixer[fe_id].enable;
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_enum *)
+ kcontrol->private_value)->shift_l;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ channel_mixer[fe_id].enable = ucontrol->value.enumerated.item[0];
+ pr_debug("%s: %s FE %d\n", __func__,
+ channel_mixer[fe_id].enable ? "Enable" : "Disable",
+ fe_id);
+ return 0;
+}
+
+static int msm_pcm_channel_input_be_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ uinfo->value.enumerated.items = ARRAY_SIZE(be_name);
+ msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts);
+
+ return 0;
+}
+
+static int msm_pcm_channel_input_be_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u16 fe_id = 0, in_ch = 0;
+
+ fe_id = e->shift_l;
+ in_ch = e->shift_r;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+ if (in_ch >= ADM_MAX_CHANNELS) {
+ pr_err("%s: invalid input channel %d\n", __func__, in_ch);
+ return -EINVAL;
+ }
+
+ channel_input[fe_id][in_ch] = ucontrol->value.enumerated.item[0];
+ return 1;
+}
+
+static int msm_pcm_channel_input_be_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u16 fe_id = 0, in_ch = 0;
+
+ fe_id = e->shift_l;
+ in_ch = e->shift_r;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+ if (in_ch >= ADM_MAX_CHANNELS) {
+ pr_err("%s: invalid input channel %d\n", __func__, in_ch);
+ return -EINVAL;
+ }
+
+ ucontrol->value.enumerated.item[0] = channel_input[fe_id][in_ch];
+ return 1;
+}
+
+
+static int msm_pcm_channel_weight_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = ADM_MAX_CHANNELS;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = WEIGHT_0_DB;
+
+ return 0;
+}
+
+static int msm_pcm_channel_weight_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0, out_ch = 0;
+ int i, weight;
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ out_ch = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->rshift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+ if (out_ch >= ADM_MAX_CHANNELS) {
+ pr_err("%s: invalid input channel %d\n", __func__, out_ch);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: FE_ID: %d, channel weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld\n",
+ __func__, fe_id,
+ ucontrol->value.integer.value[0],
+ ucontrol->value.integer.value[1],
+ ucontrol->value.integer.value[2],
+ ucontrol->value.integer.value[3],
+ ucontrol->value.integer.value[4],
+ ucontrol->value.integer.value[5],
+ ucontrol->value.integer.value[6],
+ ucontrol->value.integer.value[7]);
+
+ for (i = 0; i < ADM_MAX_CHANNELS; ++i) {
+ weight = ucontrol->value.integer.value[i];
+ channel_mixer[fe_id].channel_weight[out_ch][i] = weight;
+ pr_debug("%s: FE_ID %d, output %d input %d weight %d\n",
+ __func__, fe_id, out_ch, i,
+ channel_mixer[fe_id].channel_weight[out_ch][i]);
+ }
+
+ return 0;
+}
+
+static int msm_pcm_channel_weight_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0, out_ch = 0;
+ int i;
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ out_ch = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->rshift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+ if (out_ch >= ADM_MAX_CHANNELS) {
+ pr_err("%s: invalid input channel %d\n", __func__, out_ch);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ADM_MAX_CHANNELS; ++i)
+ ucontrol->value.integer.value[i] =
+ channel_mixer[fe_id].channel_weight[out_ch][i];
+
+ pr_debug("%s: FE_ID: %d, weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld",
+ __func__, fe_id,
+ ucontrol->value.integer.value[0],
+ ucontrol->value.integer.value[1],
+ ucontrol->value.integer.value[2],
+ ucontrol->value.integer.value[3],
+ ucontrol->value.integer.value[4],
+ ucontrol->value.integer.value[5],
+ ucontrol->value.integer.value[6],
+ ucontrol->value.integer.value[7]);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new channel_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia2 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia3 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia4 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia5 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia6 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+
+ SOC_SINGLE_EXT("MultiMedia1 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia2 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia3 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia4 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia5 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia6 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel Mixer",
+ .info = msm_pcm_channel_mixer_info,
+ .get = msm_pcm_channel_mixer_get,
+ .put = msm_pcm_channel_mixer_put,
+ .private_value = (unsigned long)&(mm1_channel_mux)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia2 Channel Mixer",
+ .info = msm_pcm_channel_mixer_info,
+ .get = msm_pcm_channel_mixer_get,
+ .put = msm_pcm_channel_mixer_put,
+ .private_value = (unsigned long)&(mm2_channel_mux)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia3 Channel Mixer",
+ .info = msm_pcm_channel_mixer_info,
+ .get = msm_pcm_channel_mixer_get,
+ .put = msm_pcm_channel_mixer_put,
+ .private_value = (unsigned long)&(mm3_channel_mux)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia4 Channel Mixer",
+ .info = msm_pcm_channel_mixer_info,
+ .get = msm_pcm_channel_mixer_get,
+ .put = msm_pcm_channel_mixer_put,
+ .private_value = (unsigned long)&(mm4_channel_mux)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel1",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 0,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel2",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 1, }
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel3",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 2,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel4",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 3,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel5",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 4,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel6",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 5,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel7",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 6,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel8",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 7,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia2 Output Channel1",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 0,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia2 Output Channel2",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 1,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia2 Output Channel3",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 2,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia3 Output Channel1",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 0,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia3 Output Channel2",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 1,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel1",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch1_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel2",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch2_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel3",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch3_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel4",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch4_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel5",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch5_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel6",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch6_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel7",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch7_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel8",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch8_enum)
+ },
+};
static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -14809,6 +15518,9 @@ static int msm_routing_probe(struct snd_soc_platform *platform)
snd_soc_add_platform_controls(platform, ec_ref_param_controls,
ARRAY_SIZE(ec_ref_param_controls));
+ snd_soc_add_platform_controls(platform, channel_mixer_controls,
+ ARRAY_SIZE(channel_mixer_controls));
+
msm_qti_pp_add_controls(platform);
msm_dts_srs_tm_add_controls(platform);
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 7cf19a9ed335..61dd478b97e4 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -520,6 +520,267 @@ fail_cmd:
return ret;
}
+static int adm_populate_channel_weight(u16 *ptr,
+ struct msm_pcm_channel_mixer *ch_mixer,
+ int channel_index)
+{
+ u16 i, j, start_index = 0;
+
+ if (channel_index > ch_mixer->output_channel) {
+ pr_err("%s: channel index %d is larger than output_channel %d\n",
+ __func__, channel_index, ch_mixer->output_channel);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ch_mixer->output_channel; i++) {
+ pr_debug("%s: weight for output %d:", __func__, i);
+ for (j = 0; j < ADM_MAX_CHANNELS; j++)
+ pr_debug(" %d",
+ ch_mixer->channel_weight[i][j]);
+ pr_debug("\n");
+ }
+
+ for (i = 0; i < channel_index; ++i)
+ start_index += ch_mixer->input_channels[i];
+
+ for (i = 0; i < ch_mixer->output_channel; ++i) {
+ for (j = start_index;
+ j < start_index +
+ ch_mixer->input_channels[channel_index]; j++) {
+ *ptr = ch_mixer->channel_weight[i][j];
+ pr_debug("%s: ptr[%d][%d] = %d\n",
+ __func__, i, j, *ptr);
+ ptr++;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * adm_programable_channel_mixer
+ *
+ * Receives port_id, copp_idx, session_id, session_type, ch_mixer
+ * and channel_index to send ADM command to mix COPP data.
+ *
+ * port_id - Passed value, port_id for which backend is wanted
+ * copp_idx - Passed value, copp_idx for which COPP is wanted
+ * session_id - Passed value, session_id for which session is needed
+ * session_type - Passed value, session_type for RX or TX
+ * ch_mixer - Passed value, ch_mixer for which channel mixer config is needed
+ * channel_index - Passed value, channel_index for which channel is needed
+ */
+int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id,
+ int session_type,
+ struct msm_pcm_channel_mixer *ch_mixer,
+ int channel_index)
+{
+ struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL;
+ struct adm_param_data_v5 data_v5;
+ int ret = 0, port_idx, sz = 0, param_size = 0;
+ u16 *adm_pspd_params;
+ u16 *ptr;
+ int index = 0;
+
+ pr_debug("%s: port_id = %d\n", __func__, port_id);
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ return -EINVAL;
+ }
+ /*
+ * First 8 bytes are 4 bytes as rule number, 2 bytes as output
+ * channel and 2 bytes as input channel.
+ * 2 * ch_mixer->output_channel means output channel mapping.
+ * 2 * ch_mixer->input_channels[channel_index]) means input
+ * channel mapping.
+ * 2 * ch_mixer->input_channels[channel_index] *
+ * ch_mixer->output_channel) means the channel mixer weighting
+ * coefficients.
+ * param_size needs to be a multiple of 4 bytes.
+ */
+
+ param_size = 2 * (4 + ch_mixer->output_channel +
+ ch_mixer->input_channels[channel_index] +
+ ch_mixer->input_channels[channel_index] *
+ ch_mixer->output_channel);
+ roundup(param_size, 4);
+
+ sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) +
+ sizeof(struct default_chmixer_param_id_coeff) +
+ sizeof(struct adm_param_data_v5) + param_size;
+ pr_debug("%s: sz = %d\n", __func__, sz);
+ adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params)
+ return -ENOMEM;
+
+ adm_params->payload_addr_lsw = 0;
+ adm_params->payload_addr_msw = 0;
+ adm_params->mem_map_handle = 0;
+ adm_params->direction = session_type;
+ adm_params->sessionid = session_id;
+ pr_debug("%s: copp_id = %d, session id %d\n", __func__,
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]),
+ session_id);
+ adm_params->deviceid = atomic_read(
+ &this_adm.copp.id[port_idx][copp_idx]);
+ adm_params->reserved = 0;
+
+ data_v5.module_id = MTMX_MODULE_ID_DEFAULT_CHMIXER;
+ data_v5.param_id = DEFAULT_CHMIXER_PARAM_ID_COEFF;
+ data_v5.reserved = 0;
+ data_v5.param_size = param_size;
+ adm_params->payload_size =
+ sizeof(struct default_chmixer_param_id_coeff) +
+ sizeof(struct adm_param_data_v5) + data_v5.param_size;
+ adm_pspd_params = (u16 *)((u8 *)adm_params +
+ sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5));
+ memcpy(adm_pspd_params, &data_v5, sizeof(data_v5));
+
+ adm_pspd_params = (u16 *)((u8 *)adm_params +
+ sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)
+ + sizeof(data_v5));
+
+ adm_pspd_params[0] = ch_mixer->rule;
+ adm_pspd_params[2] = ch_mixer->output_channel;
+ adm_pspd_params[3] = ch_mixer->input_channels[channel_index];
+ index = 4;
+
+ if (ch_mixer->output_channel == 1) {
+ adm_pspd_params[index] = PCM_CHANNEL_FC;
+ } else if (ch_mixer->output_channel == 2) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ } else if (ch_mixer->output_channel == 3) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_FC;
+ } else if (ch_mixer->output_channel == 4) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->output_channel == 5) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->output_channel == 6) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LFE;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 5] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->output_channel == 8) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LFE;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 5] = PCM_CHANNEL_RS;
+ adm_pspd_params[index + 6] = PCM_CHANNEL_LB;
+ adm_pspd_params[index + 7] = PCM_CHANNEL_RB;
+ }
+
+ index = index + ch_mixer->output_channel;
+ if (ch_mixer->input_channels[channel_index] == 1) {
+ adm_pspd_params[index] = PCM_CHANNEL_FC;
+ } else if (ch_mixer->input_channels[channel_index] == 2) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ } else if (ch_mixer->input_channels[channel_index] == 3) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_FC;
+ } else if (ch_mixer->input_channels[channel_index] == 4) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->input_channels[channel_index] == 5) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->input_channels[channel_index] == 6) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LFE;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 5] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->input_channels[channel_index] == 8) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LFE;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 5] = PCM_CHANNEL_RS;
+ adm_pspd_params[index + 6] = PCM_CHANNEL_LB;
+ adm_pspd_params[index + 7] = PCM_CHANNEL_RB;
+ }
+
+ index = index + ch_mixer->input_channels[channel_index];
+ ret = adm_populate_channel_weight(&adm_pspd_params[index],
+ ch_mixer, channel_index);
+ if (!ret) {
+ pr_err("%s: fail to get channel weight with error %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+
+ adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ adm_params->hdr.src_svc = APR_SVC_ADM;
+ adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+ adm_params->hdr.src_port = port_id;
+ adm_params->hdr.dest_svc = APR_SVC_ADM;
+ adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_params->hdr.dest_port =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ adm_params->hdr.token = port_idx << 16 | copp_idx;
+ adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5;
+ adm_params->hdr.pkt_size = sz;
+ adm_params->payload_addr_lsw = 0;
+ adm_params->payload_addr_msw = 0;
+ adm_params->mem_map_handle = 0;
+ adm_params->reserved = 0;
+
+ ptr = (u16 *)adm_params;
+ for (index = 0; index < (sz / 2); index++)
+ pr_debug("%s: adm_params[%d] = 0x%x\n",
+ __func__, index, (unsigned int)ptr[index]);
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+ if (ret < 0) {
+ pr_err("%s: Set params failed port %d rc %d\n", __func__,
+ port_id, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ 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: set params timed out port = %d\n",
+ __func__, port_id);
+ ret = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ ret = 0;
+fail_cmd:
+ kfree(adm_params);
+
+ return ret;
+}
+
int adm_set_stereo_to_custom_stereo(int port_id, int copp_idx,
unsigned int session_id, char *params,
uint32_t params_length)
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index da156bf61610..6f097b7e8dd5 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -3399,11 +3399,12 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac,
open->shared_circ_buf_start_phy_addr_lsw =
lower_32_bits(buf_circ->phys);
open->shared_circ_buf_start_phy_addr_msw =
- upper_32_bits(buf_circ->phys);
+ msm_audio_populate_upper_32_bits(buf_circ->phys);
open->shared_circ_buf_size = bufsz * bufcnt;
open->map_region_circ_buf.shm_addr_lsw = lower_32_bits(buf_circ->phys);
- open->map_region_circ_buf.shm_addr_msw = upper_32_bits(buf_circ->phys);
+ open->map_region_circ_buf.shm_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_circ->phys);
open->map_region_circ_buf.mem_size_bytes = bytes_to_alloc;
mutex_unlock(&ac->cmd_lock);
@@ -3445,10 +3446,12 @@ int q6asm_set_shared_pos_buff(struct audio_client *ac,
open->shared_pos_buf_num_regions = 1;
open->shared_pos_buf_property_flag = 0x00;
open->shared_pos_buf_phy_addr_lsw = lower_32_bits(buf_pos->phys);
- open->shared_pos_buf_phy_addr_msw = upper_32_bits(buf_pos->phys);
+ open->shared_pos_buf_phy_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_pos->phys);
open->map_region_pos_buf.shm_addr_lsw = lower_32_bits(buf_pos->phys);
- open->map_region_pos_buf.shm_addr_msw = upper_32_bits(buf_pos->phys);
+ open->map_region_pos_buf.shm_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_pos->phys);
open->map_region_pos_buf.mem_size_bytes = bytes_to_alloc;
done:
diff --git a/tools/testing/selftests/x86/ldt_gdt.c b/tools/testing/selftests/x86/ldt_gdt.c
index 31a3035cd4eb..923e59eb82c7 100644
--- a/tools/testing/selftests/x86/ldt_gdt.c
+++ b/tools/testing/selftests/x86/ldt_gdt.c
@@ -394,6 +394,51 @@ static void *threadproc(void *ctx)
}
}
+#ifdef __i386__
+
+#ifndef SA_RESTORE
+#define SA_RESTORER 0x04000000
+#endif
+
+/*
+ * The UAPI header calls this 'struct sigaction', which conflicts with
+ * glibc. Sigh.
+ */
+struct fake_ksigaction {
+ void *handler; /* the real type is nasty */
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+ unsigned char sigset[8];
+};
+
+static void fix_sa_restorer(int sig)
+{
+ struct fake_ksigaction ksa;
+
+ if (syscall(SYS_rt_sigaction, sig, NULL, &ksa, 8) == 0) {
+ /*
+ * glibc has a nasty bug: it sometimes writes garbage to
+ * sa_restorer. This interacts quite badly with anything
+ * that fiddles with SS because it can trigger legacy
+ * stack switching. Patch it up. See:
+ *
+ * https://sourceware.org/bugzilla/show_bug.cgi?id=21269
+ */
+ if (!(ksa.sa_flags & SA_RESTORER) && ksa.sa_restorer) {
+ ksa.sa_restorer = NULL;
+ if (syscall(SYS_rt_sigaction, sig, &ksa, NULL,
+ sizeof(ksa.sigset)) != 0)
+ err(1, "rt_sigaction");
+ }
+ }
+}
+#else
+static void fix_sa_restorer(int sig)
+{
+ /* 64-bit glibc works fine. */
+}
+#endif
+
static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
int flags)
{
@@ -405,6 +450,7 @@ static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
if (sigaction(sig, &sa, 0))
err(1, "sigaction");
+ fix_sa_restorer(sig);
}
static jmp_buf jmpbuf;