summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/android/binder.c2
-rw-r--r--drivers/base/firmware_class.c4
-rw-r--r--drivers/base/power/runtime.c11
-rw-r--r--drivers/bluetooth/bluetooth-power.c4
-rw-r--r--drivers/bluetooth/btfm_slim_codec.c35
-rw-r--r--drivers/bluetooth/btfm_slim_wcn3990.c6
-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.c78
-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_mhi.c29
-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/pcmcia/cm4040_cs.c6
-rw-r--r--drivers/char/random.c4
-rw-r--r--drivers/char/tpm/tpm_crb.c3
-rw-r--r--drivers/clk/msm/clock-local2.c37
-rw-r--r--drivers/clk/msm/clock-osm.c180
-rw-r--r--drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c240
-rw-r--r--drivers/clk/msm/mdss/mdss-hdmi-pll.h16
-rw-r--r--drivers/clk/msm/mdss/mdss-pll.c14
-rw-r--r--drivers/clk/msm/mdss/mdss-pll.h5
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/clk-cpu-osm.c24
-rw-r--r--drivers/clk/qcom/clk-regmap-mux-div.c263
-rw-r--r--drivers/clk/qcom/clk-regmap-mux-div.h66
-rw-r--r--drivers/cpufreq/Kconfig26
-rw-r--r--drivers/cpufreq/Makefile2
-rw-r--r--drivers/cpufreq/cpufreq.c33
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c4
-rw-r--r--drivers/cpufreq/cpufreq_governor_attr_set.c84
-rw-r--r--drivers/cpuidle/lpm-levels.c17
-rw-r--r--drivers/crypto/msm/ice.c3
-rw-r--r--drivers/crypto/msm/qcrypto.c29
-rw-r--r--drivers/dma/ep93xx_dma.c2
-rw-r--r--drivers/dma/sh/usb-dmac.c2
-rw-r--r--drivers/gpio/gpiolib-acpi.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ci_dpm.c6
-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/ast/ast_drv.h1
-rw-r--r--drivers/gpu/drm/ast/ast_main.c157
-rw-r--r--drivers/gpu/drm/ast/ast_post.c18
-rw-r--r--drivers/gpu/drm/drm_edid.c8
-rw-r--r--drivers/gpu/drm/drm_mm.c248
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_lvds.c18
-rw-r--r--drivers/gpu/drm/msm/Kconfig2
-rw-r--r--drivers/gpu/drm/msm/Makefile10
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c147
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_power.c18
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_preempt.c56
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_snapshot.c29
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c94
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.h23
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_drm.c12
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_host.c5
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c966
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h148
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c34
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c230
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c994
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c827
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h167
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c41
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h37
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.xml.h18
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c80
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_i2c.c81
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_util.c143
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c2
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c2
-rw-r--r--drivers/gpu/drm/msm/msm_atomic.c19
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c172
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h63
-rw-r--r--drivers/gpu/drm/msm/msm_fbdev.c6
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c696
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h41
-rw-r--r--drivers/gpu/drm/msm/msm_gem_prime.c7
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c75
-rw-r--r--drivers/gpu/drm/msm/msm_gem_vma.c68
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c183
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h20
-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.c18
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.h29
-rw-r--r--drivers/gpu/drm/msm/msm_smmu.c50
-rw-r--r--drivers/gpu/drm/msm/msm_submitqueue.c151
-rw-r--r--drivers/gpu/drm/msm/msm_trace.h98
-rw-r--r--drivers/gpu/drm/msm/msm_trace_points.c18
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.c156
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.h24
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.c88
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.h10
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder.c16
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c27
-rw-r--r--drivers/gpu/drm/msm/sde/sde_formats.c432
-rw-r--r--drivers/gpu/drm/msm/sde/sde_formats.h15
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.c293
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.h46
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h57
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_cdm.c48
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_cdm.h5
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_interrupts.c17
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_mdss.h28
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_sspp.c9
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_util.c17
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_util.h4
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c77
-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/msm/sde_hdcp.h84
-rw-r--r--drivers/gpu/drm/msm/sde_hdcp_1x.c1722
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_usif.c3
-rw-r--r--drivers/gpu/drm/nouveau/nv84_fence.c6
-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.c64
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c2
-rw-r--r--drivers/gpu/drm/radeon/ci_dpm.c6
-rw-r--r--drivers/gpu/drm/radeon/cik.c4
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c4
-rw-r--r--drivers/gpu/drm/radeon/r600.c2
-rw-r--r--drivers/gpu/drm/radeon/si.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_surface.c21
-rw-r--r--drivers/gpu/msm/adreno.c5
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c2
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c31
-rw-r--r--drivers/gpu/msm/adreno_dispatch.h2
-rw-r--r--drivers/gpu/msm/kgsl.c50
-rw-r--r--drivers/gpu/msm/kgsl.h5
-rw-r--r--drivers/gpu/msm/kgsl_device.h5
-rw-r--r--drivers/gpu/msm/kgsl_events.c8
-rw-r--r--drivers/gpu/msm/kgsl_pool.c13
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c134
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.c65
-rw-r--r--drivers/hid/uhid.c17
-rw-r--r--drivers/hid/wacom_wac.c45
-rw-r--r--drivers/i2c/busses/i2c-msm-v2.c49
-rw-r--r--drivers/i2c/busses/i2c-piix4.c2
-rw-r--r--drivers/i2c/busses/i2c-tiny-usb.c25
-rw-r--r--drivers/iio/dac/ad7303.c6
-rw-r--r--drivers/iio/light/ltr501.c4
-rw-r--r--drivers/iio/proximity/as3935.c13
-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/hw/qib/qib_rc.c4
-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/input/mouse/elantech.c16
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/Kconfig22
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/Makefile2
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c31
-rwxr-xr-xdrivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c671
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c810
-rw-r--r--drivers/iommu/arm-smmu.c43
-rw-r--r--drivers/iommu/intel-iommu.c5
-rw-r--r--drivers/iommu/io-pgtable-arm.c31
-rw-r--r--drivers/iommu/io-pgtable.h1
-rw-r--r--drivers/iommu/iommu-debug.c8
-rw-r--r--drivers/irqchip/irq-xtensa-mx.c2
-rw-r--r--drivers/irqchip/irq-xtensa-pic.c2
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c3
-rw-r--r--drivers/md/Kconfig1
-rw-r--r--drivers/md/dm-android-verity.c2
-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/Kconfig4
-rw-r--r--drivers/media/Makefile4
-rw-r--r--drivers/media/cec-notifier.c129
-rw-r--r--drivers/media/cec/cec-core.c22
-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/ais/camera/camera.c15
-rw-r--r--drivers/media/platform/msm/ais/common/cam_hw_ops.c4
-rw-r--r--drivers/media/platform/msm/ais/common/cam_smmu_api.c2
-rw-r--r--drivers/media/platform/msm/ais/common/cam_smmu_api.h4
-rw-r--r--drivers/media/platform/msm/ais/common/cam_soc_api.c4
-rw-r--r--drivers/media/platform/msm/ais/common/msm_camera_io_util.c10
-rw-r--r--drivers/media/platform/msm/ais/common/msm_camera_io_util.h2
-rw-r--r--drivers/media/platform/msm/ais/fd/msm_fd_dev.c19
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_buf_mgr.c30
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp.c17
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp47.c1
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp47.h4
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c6
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h16
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c8
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_stats_util.h2
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_util.c18
-rw-r--r--drivers/media/platform/msm/ais/msm.c104
-rw-r--r--drivers/media/platform/msm/ais/msm.h5
-rw-r--r--drivers/media/platform/msm/ais/msm_buf_mgr/msm_generic_buf_mgr.c10
-rw-r--r--drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c145
-rw-r--r--drivers/media/platform/msm/ais/msm_vb2/msm_vb2.h1
-rw-r--r--drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c17
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/msm_csid.c17
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h2
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h2
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h2
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h2
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h2
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h4
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c6
-rw-r--r--drivers/media/platform/msm/ais/sensor/flash/msm_flash.c18
-rw-r--r--drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c11
-rw-r--r--drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c2
-rw-r--r--drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h3
-rw-r--r--drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c22
-rw-r--r--drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c16
-rw-r--r--drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h8
-rw-r--r--drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c4
-rw-r--r--drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c4
-rw-r--r--drivers/media/platform/msm/ais/sensor/msm_sensor.c45
-rw-r--r--drivers/media/platform/msm/ais/sensor/msm_sensor.h2
-rw-r--r--drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c32
-rw-r--r--drivers/media/platform/msm/ais/sensor/ois/msm_ois.c15
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c3
-rw-r--r--drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c19
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.h2
-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.c51
-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.c13
-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/camera_v2/sensor/actuator/msm_actuator.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c12
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c8
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c16
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c16
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c4
-rw-r--r--drivers/media/platform/msm/sde/Kconfig12
-rw-r--r--drivers/media/platform/msm/sde/Makefile1
-rw-r--r--drivers/media/platform/msm/sde/cec/Makefile3
-rw-r--r--drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c429
-rw-r--r--drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.c743
-rw-r--r--drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.h93
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.c124
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.h12
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c9
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c92
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h7
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c4
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c16
-rw-r--r--drivers/media/platform/msm/vidc/hfi_packetization.c6
-rw-r--r--drivers/media/platform/msm/vidc/msm_smem.c15
-rw-r--r--drivers/media/platform/msm/vidc/msm_v4l2_vidc.c12
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c70
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.h17
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c48
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c273
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c28
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.h5
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_dcvs.c8
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c302
-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/msm_vidc_res_parse.c7
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c2
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_api.h4
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_helper.h4
-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/pvrusb2/pvrusb2-eeprom.c13
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c9
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c8
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c2
-rw-r--r--drivers/mfd/omap-usb-tll.c2
-rw-r--r--drivers/misc/Kconfig2
-rw-r--r--drivers/misc/c2port/c2port-duramar2150.c4
-rw-r--r--drivers/misc/cxl/file.c7
-rw-r--r--drivers/misc/hdcp.c303
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c58
-rw-r--r--drivers/misc/qseecom.c30
-rw-r--r--drivers/misc/uid_sys_stats.c117
-rw-r--r--drivers/mmc/card/block.c2
-rw-r--r--drivers/mmc/core/core.c45
-rw-r--r--drivers/mmc/core/debugfs.c11
-rw-r--r--drivers/mmc/core/host.c2
-rw-r--r--drivers/mmc/core/sd.c5
-rw-r--r--drivers/mmc/host/sdhci-iproc.c3
-rw-r--r--drivers/mmc/host/sdhci-msm.c43
-rw-r--r--drivers/mmc/host/sdhci-msm.h3
-rw-r--r--drivers/mmc/host/sdhci.c6
-rw-r--r--drivers/net/can/usb/gs_usb.c2
-rw-r--r--drivers/net/ethernet/adaptec/starfire.c45
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c4
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c4
-rw-r--r--drivers/net/ethernet/ethoc.c3
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c4
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_enet.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/catas.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/intf.c12
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h1
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c112
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_emaclite.c126
-rw-r--r--drivers/net/hamradio/mkiss.c4
-rw-r--r--drivers/net/irda/irda-usb.c2
-rw-r--r--drivers/net/phy/marvell.c66
-rw-r--r--drivers/net/usb/qmi_wwan.c3
-rw-r--r--drivers/net/usb/r8152.c13
-rw-r--r--drivers/net/usb/sierra_net.c111
-rw-r--r--drivers/net/virtio_net.c1
-rw-r--r--drivers/net/vxlan.c19
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c3
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h6
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c143
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c29
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h4
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c37
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h12
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c4
-rw-r--r--drivers/net/wireless/ath/regd.c18
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c553
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c6
-rw-r--r--drivers/net/wireless/ath/wil6210/ioctl.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c17
-rw-r--r--drivers/net/wireless/ath/wil6210/pm.c13
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c5
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h4
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c4
-rw-r--r--drivers/net/wireless/cnss/Kconfig1
-rw-r--r--drivers/net/wireless/cnss/cnss_pci.c62
-rw-r--r--drivers/net/wireless/cnss_utils/Kconfig6
-rw-r--r--drivers/net/wireless/cnss_utils/Makefile1
-rw-r--r--drivers/net/wireless/cnss_utils/cnss_utils.c310
-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/net/xen-netfront.c4
-rw-r--r--drivers/of/address.c2
-rw-r--r--drivers/parport/parport_gsc.c8
-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/perf/arm_pmu.c1
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-v3.h12
-rw-r--r--drivers/pinctrl/berlin/berlin-bg4ct.c2
-rw-r--r--drivers/platform/msm/gsi/gsi.c29
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c71
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_rt.c6
-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_v2/rmnet_ipa.c12
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_client.c17
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c68
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c52
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c18
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_rt.c7
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c5
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c15
-rw-r--r--drivers/platform/msm/ipa/test/ipa_test_mhi.c22
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.c120
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.h1
-rw-r--r--drivers/platform/msm/mhi/mhi_iface.c49
-rw-r--r--drivers/platform/msm/mhi/mhi_macros.h7
-rw-r--r--drivers/platform/msm/mhi/mhi_main.c38
-rw-r--r--drivers/platform/msm/mhi/mhi_pm.c105
-rw-r--r--drivers/platform/msm/mhi/mhi_states.c29
-rw-r--r--drivers/platform/msm/mhi_uci/mhi_uci.c200
-rw-r--r--drivers/platform/msm/msm_ext_display.c8
-rw-r--r--drivers/platform/msm/sps/sps.c15
-rw-r--r--drivers/platform/msm/sps/spsi.h19
-rw-r--r--drivers/platform/msm/usb_bam.c81
-rw-r--r--drivers/power/power_supply_sysfs.c2
-rw-r--r--drivers/power/qcom/debug_core.c6
-rw-r--r--drivers/power/reset/msm-poweroff.c17
-rw-r--r--drivers/power/supply/qcom/battery.c2
-rw-r--r--drivers/power/supply/qcom/fg-core.h20
-rw-r--r--drivers/power/supply/qcom/fg-util.c41
-rw-r--r--drivers/power/supply/qcom/qpnp-fg-gen3.c379
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c34
-rw-r--r--drivers/power/supply/qcom/smb-lib.c252
-rw-r--r--drivers/power/supply/qcom/smb-lib.h5
-rw-r--r--drivers/power/supply/qcom/smb1351-charger.c4
-rw-r--r--drivers/power/supply/qcom/smb138x-charger.c7
-rw-r--r--drivers/power/supply/qcom/storm-watch.c10
-rw-r--r--drivers/power/supply/qcom/storm-watch.h1
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/cpr3-hmss-regulator.c4
-rw-r--r--drivers/regulator/cpr3-regulator.c3
-rw-r--r--drivers/regulator/cprh-kbss-regulator.c2
-rw-r--r--drivers/regulator/max20010-regulator.c490
-rw-r--r--drivers/regulator/qpnp-regulator.c99
-rw-r--r--drivers/regulator/rpm-smd-regulator.c59
-rw-r--r--drivers/regulator/spm-regulator.c309
-rw-r--r--drivers/regulator/tps65023-regulator.c3
-rw-r--r--drivers/s390/net/qeth_core.h4
-rw-r--r--drivers/s390/net/qeth_core_main.c21
-rw-r--r--drivers/s390/net/qeth_core_sys.c24
-rw-r--r--drivers/s390/net/qeth_l2.h2
-rw-r--r--drivers/s390/net/qeth_l2_main.c26
-rw-r--r--drivers/s390/net/qeth_l2_sys.c8
-rw-r--r--drivers/s390/net/qeth_l3_main.c6
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c15
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c8
-rw-r--r--drivers/scsi/ufs/ufs-qcom-ice.c42
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c21
-rw-r--r--drivers/scsi/ufs/ufs-qcom.h1
-rw-r--r--drivers/scsi/ufs/ufshcd.c58
-rw-r--r--drivers/soc/qcom/Kconfig1
-rw-r--r--drivers/soc/qcom/glink.c117
-rw-r--r--drivers/soc/qcom/glink_private.h2
-rw-r--r--drivers/soc/qcom/glink_smem_native_xprt.c72
-rw-r--r--drivers/soc/qcom/glink_ssr.c18
-rw-r--r--drivers/soc/qcom/icnss.c417
-rw-r--r--drivers/soc/qcom/icnss_utils.c28
-rw-r--r--drivers/soc/qcom/ipc_router_glink_xprt.c21
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c5
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_rules.c2
-rw-r--r--drivers/soc/qcom/msm_glink_pkt.c31
-rw-r--r--drivers/soc/qcom/peripheral-loader.c9
-rw-r--r--drivers/soc/qcom/pil-msa.c4
-rw-r--r--drivers/soc/qcom/pil-q6v5.c2
-rw-r--r--drivers/soc/qcom/rpm-smd-debug.c26
-rw-r--r--drivers/soc/qcom/rpm_master_stat.c50
-rw-r--r--drivers/soc/qcom/rpm_rail_stats.c53
-rw-r--r--drivers/soc/qcom/rpm_stats.c76
-rw-r--r--drivers/soc/qcom/scm.c9
-rw-r--r--drivers/soc/qcom/smp2p_sleepstate.c6
-rw-r--r--drivers/soc/qcom/spcom.c2
-rw-r--r--drivers/soc/qcom/subsys-pil-tz.c1
-rw-r--r--drivers/soc/qcom/subsystem_restart.c3
-rw-r--r--drivers/soc/qcom/wcd-dsp-glink.c58
-rwxr-xr-xdrivers/soundwire/soundwire.c71
-rw-r--r--drivers/soundwire/swr-wcd-ctrl.c16
-rw-r--r--drivers/spi/spi_qsd.c17
-rw-r--r--drivers/staging/android/lowmemorykiller.c3
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.c13
-rw-r--r--drivers/staging/gdm724x/gdm_mux.c4
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_pack.c9
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_ap.c2
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c24
-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.c27
-rw-r--r--drivers/thermal/msm-tsens.c2
-rw-r--r--drivers/thermal/msm_thermal.c2
-rw-r--r--drivers/thermal/qpnp-adc-tm.c61
-rw-r--r--drivers/tty/pty.c7
-rw-r--r--drivers/tty/serial/efm32-uart.c11
-rw-r--r--drivers/tty/serial/ifx6x60.c2
-rw-r--r--drivers/tty/serial/omap-serial.c9
-rw-r--r--drivers/tty/serial/samsung.c9
-rw-r--r--drivers/tty/serial/sh-sci.c10
-rw-r--r--drivers/tty/tty_io.c3
-rw-r--r--drivers/tty/tty_mutex.c7
-rw-r--r--drivers/usb/chipidea/debug.c3
-rw-r--r--drivers/usb/chipidea/udc.c8
-rw-r--r--drivers/usb/class/cdc-acm.c13
-rw-r--r--drivers/usb/core/config.c8
-rw-r--r--drivers/usb/core/driver.c21
-rw-r--r--drivers/usb/core/file.c9
-rw-r--r--drivers/usb/core/generic.c38
-rw-r--r--drivers/usb/core/hcd.c1
-rw-r--r--drivers/usb/core/hub.c38
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c4
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c13
-rw-r--r--drivers/usb/gadget/configfs.c46
-rw-r--r--drivers/usb/gadget/function/f_audio_source.c4
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/usb/gadget/function/f_gsi.c4
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c13
-rw-r--r--drivers/usb/gadget/function/f_mtp.c4
-rw-r--r--drivers/usb/gadget/legacy/inode.c5
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c19
-rw-r--r--drivers/usb/gadget/udc/net2280.c9
-rw-r--r--drivers/usb/host/r8a66597-hcd.c6
-rw-r--r--drivers/usb/host/xhci-hub.c12
-rw-r--r--drivers/usb/host/xhci-mem.c4
-rw-r--r--drivers/usb/host/xhci-pci.c10
-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.c114
-rw-r--r--drivers/usb/pd/qpnp-pdphy.c32
-rw-r--r--drivers/usb/pd/usbpd.h7
-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/core/fbmem.c7
-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_dba_utils.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c9
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c37
-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.c59
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c19
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c9
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c47
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c41
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c52
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.h6
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c29
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_util.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c38
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c12
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c14
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c50
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c11
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_splash_logo.c47
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_util.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_rotator.c2
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.h7
-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.c647
-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.c614
-rw-r--r--drivers/watchdog/pcwd_usb.c3
-rw-r--r--drivers/xen/privcmd.c4
614 files changed, 24956 insertions, 7589 deletions
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/android/binder.c b/drivers/android/binder.c
index d1490be45c67..0c3cf182e351 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -3778,7 +3778,9 @@ static void binder_deferred_release(struct binder_proc *proc)
page_count++;
}
kfree(proc->pages);
+ preempt_enable_no_resched();
vfree(proc->buffer);
+ preempt_disable();
}
put_task_struct(proc->tsk);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 212ca2eee257..a1696e1d199f 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;
}
@@ -310,7 +311,8 @@ static const char * const fw_path[] = {
"/lib/firmware/updates/" UTS_RELEASE,
"/lib/firmware/updates",
"/lib/firmware/" UTS_RELEASE,
- "/lib/firmware"
+ "/lib/firmware",
+ "/lib64/firmware"
};
/*
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 3252429f96af..3a20dc594338 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -889,13 +889,13 @@ int __pm_runtime_idle(struct device *dev, int rpmflags)
unsigned long flags;
int retval;
- might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
-
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count))
return 0;
}
+ might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
+
spin_lock_irqsave(&dev->power.lock, flags);
retval = rpm_idle(dev, rpmflags);
spin_unlock_irqrestore(&dev->power.lock, flags);
@@ -921,13 +921,13 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags)
unsigned long flags;
int retval;
- might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
-
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count))
return 0;
}
+ might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
+
spin_lock_irqsave(&dev->power.lock, flags);
retval = rpm_suspend(dev, rpmflags);
spin_unlock_irqrestore(&dev->power.lock, flags);
@@ -952,7 +952,8 @@ int __pm_runtime_resume(struct device *dev, int rpmflags)
unsigned long flags;
int retval;
- might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
+ might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe &&
+ dev->power.runtime_status != RPM_ACTIVE);
if (rpmflags & RPM_GET_PUT)
atomic_inc(&dev->power.usage_count);
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
index 59245ba320f6..99c18e3d66d7 100644
--- a/drivers/bluetooth/bluetooth-power.c
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2010, 2013-2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2010, 2013-2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -682,7 +682,7 @@ int bt_register_slimdev(struct device *dev)
static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- int ret, pwr_cntrl = 0;
+ int ret = 0, pwr_cntrl = 0;
switch (cmd) {
case BT_CMD_SLIM_TEST:
diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c
index 05da1fb1f975..96be0e2f9183 100644
--- a/drivers/bluetooth/btfm_slim_codec.c
+++ b/drivers/bluetooth/btfm_slim_codec.c
@@ -26,6 +26,9 @@
#include <sound/tlv.h>
#include <btfm_slim.h>
+static int bt_soc_enable_status;
+
+
static int btfm_slim_codec_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
@@ -38,8 +41,31 @@ static unsigned int btfm_slim_codec_read(struct snd_soc_codec *codec,
return 0;
}
+static int bt_soc_status_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = bt_soc_enable_status;
+ return 1;
+}
+
+static int bt_soc_status_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 1;
+}
+
+static const struct snd_kcontrol_new status_controls[] = {
+ SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0,
+ bt_soc_status_get,
+ bt_soc_status_put)
+
+};
+
+
static int btfm_slim_codec_probe(struct snd_soc_codec *codec)
{
+ snd_soc_add_codec_controls(codec, status_controls,
+ ARRAY_SIZE(status_controls));
return 0;
}
@@ -130,6 +156,7 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream,
struct btfmslim *btfmslim = dai->dev->platform_data;
struct btfmslim_ch *ch;
uint8_t rxport, grp = false, nchan = 1;
+ bt_soc_enable_status = 0;
BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
dai->id, dai->rate);
@@ -171,6 +198,10 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream,
}
ret = btfm_slim_enable_ch(btfmslim, ch, rxport, dai->rate, grp, nchan);
+
+ /* save the enable channel status */
+ if (ret == 0)
+ bt_soc_enable_status = 1;
return ret;
}
@@ -296,9 +327,9 @@ static int btfm_slim_dai_get_channel_map(struct snd_soc_dai *dai,
unsigned int *tx_num, unsigned int *tx_slot,
unsigned int *rx_num, unsigned int *rx_slot)
{
- int i, ret = -EINVAL, *slot, j = 0, num = 1;
+ int i, ret = -EINVAL, *slot = NULL, j = 0, num = 1;
struct btfmslim *btfmslim = dai->dev->platform_data;
- struct btfmslim_ch *ch;
+ struct btfmslim_ch *ch = NULL;
if (!btfmslim)
return ret;
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c
index 77e2973e023c..0c4e0b3d5c2e 100644
--- a/drivers/bluetooth/btfm_slim_wcn3990.c
+++ b/drivers/bluetooth/btfm_slim_wcn3990.c
@@ -88,12 +88,12 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable);
if (rxport) {
- if (enable && btfmslim->sample_rate == 48000) {
- /* For A2DP Rx */
+ if (enable) {
+ /* For SCO Rx, A2DP Rx */
reg_val = 0x1;
port_bit = port_num - 0x10;
reg = CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_0(port_bit);
- BTFMSLIM_DBG("writing reg_val (%d) to reg(%x) for A2DP",
+ BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)",
reg_val, reg);
ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
if (ret) {
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..20e617ed0770 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;
@@ -60,7 +61,8 @@ static const struct diag_ssid_range_t msg_mask_tbl[] = {
{ .ssid_first = MSG_SSID_21, .ssid_last = MSG_SSID_21_LAST },
{ .ssid_first = MSG_SSID_22, .ssid_last = MSG_SSID_22_LAST },
{ .ssid_first = MSG_SSID_23, .ssid_last = MSG_SSID_23_LAST },
- { .ssid_first = MSG_SSID_24, .ssid_last = MSG_SSID_24_LAST }
+ { .ssid_first = MSG_SSID_24, .ssid_last = MSG_SSID_24_LAST },
+ { .ssid_first = MSG_SSID_25, .ssid_last = MSG_SSID_25_LAST }
};
static int diag_apps_responds(void)
@@ -89,7 +91,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 +108,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 +211,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 +236,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 +314,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 +331,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_mhi.c b/drivers/char/diag/diagfwd_mhi.c
index 8b0e1f32bdc5..03133a5a89aa 100644
--- a/drivers/char/diag/diagfwd_mhi.c
+++ b/drivers/char/diag/diagfwd_mhi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -553,6 +553,8 @@ static void mhi_notifier(struct mhi_cb_info *cb_info)
struct mhi_result *result = NULL;
struct diag_mhi_ch_t *ch = NULL;
void *buf = NULL;
+ struct diag_mhi_info *mhi_info = NULL;
+ unsigned long flags;
if (!cb_info)
return;
@@ -604,13 +606,6 @@ static void mhi_notifier(struct mhi_cb_info *cb_info)
queue_work(diag_mhi[index].mhi_wq,
&(diag_mhi[index].open_work));
break;
- case MHI_CB_MHI_DISABLED:
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
- "received mhi disabled notifiation port: %d ch: %d\n",
- index, ch->type);
- atomic_set(&(ch->opened), 0);
- __mhi_close(&diag_mhi[index], CHANNELS_CLOSED);
- break;
case MHI_CB_XFER:
/*
* If the channel is a read channel, this is a read
@@ -637,6 +632,24 @@ static void mhi_notifier(struct mhi_cb_info *cb_info)
result->bytes_xferd,
diag_mhi[index].id);
break;
+ case MHI_CB_MHI_DISABLED:
+ case MHI_CB_SYS_ERROR:
+ case MHI_CB_MHI_SHUTDOWN:
+ DIAG_LOG(DIAG_DEBUG_BRIDGE,
+ "received mhi link down cb: %d port: %d ch: %d\n",
+ cb_info->cb_reason, index, ch->type);
+ mhi_info = &diag_mhi[index];
+ if (!mhi_info->enabled)
+ return;
+ spin_lock_irqsave(&mhi_info->lock, flags);
+ mhi_info->enabled = 0;
+ spin_unlock_irqrestore(&mhi_info->lock, flags);
+ atomic_set(&(mhi_info->read_ch.opened), 0);
+ atomic_set(&(mhi_info->write_ch.opened), 0);
+ flush_workqueue(mhi_info->mhi_wq);
+ mhi_buf_tbl_clear(mhi_info);
+ diag_remote_dev_close(mhi_info->dev_id);
+ break;
default:
pr_err("diag: In %s, invalid cb reason 0x%x\n", __func__,
cb_info->cb_reason);
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..2898d19fadf5 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 - 1 < offset)
+ return -EINVAL;
if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
return -EINVAL;
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
index fc061f7c2bd1..a7de8ae185a5 100644
--- a/drivers/char/pcmcia/cm4040_cs.c
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -374,7 +374,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf,
rc = write_sync_reg(SCR_HOST_TO_READER_START, dev);
if (rc <= 0) {
- DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc);
+ DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc);
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
@@ -387,7 +387,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf,
for (i = 0; i < bytes_to_write; i++) {
rc = wait_for_bulk_out_ready(dev);
if (rc <= 0) {
- DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2Zx\n",
+ DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2zx\n",
rc);
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
if (rc == -ERESTARTSYS)
@@ -403,7 +403,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf,
rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev);
if (rc <= 0) {
- DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc);
+ DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc);
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
diff --git a/drivers/char/random.c b/drivers/char/random.c
index d93dfebae0bb..1822472dffab 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1798,13 +1798,15 @@ int random_int_secret_init(void)
return 0;
}
+static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash)
+ __aligned(sizeof(unsigned long));
+
/*
* Get a random word for internal kernel use only. Similar to urandom but
* with the goal of minimal entropy pool depletion. As a result, the random
* value is not cryptographically secure but for several uses the cost of
* depleting entropy is too high
*/
-static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
unsigned int get_random_int(void)
{
__u32 *hash;
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-local2.c b/drivers/clk/msm/clock-local2.c
index adb07cdb7e8d..076ead6aaf34 100644
--- a/drivers/clk/msm/clock-local2.c
+++ b/drivers/clk/msm/clock-local2.c
@@ -1339,34 +1339,6 @@ static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */
{0, 0},
};
-static bool is_same_rcg_config(struct rcg_clk *rcg, struct clk_freq_tbl *freq,
- bool has_mnd)
-{
- u32 cfg;
-
- /* RCG update pending */
- if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_CONFIG_DIRTY_MASK)
- return false;
- if (has_mnd)
- if (readl_relaxed(M_REG(rcg)) != freq->m_val ||
- readl_relaxed(N_REG(rcg)) != freq->n_val ||
- readl_relaxed(D_REG(rcg)) != freq->d_val)
- return false;
- /*
- * Both 0 and 1 represent same divider value in HW.
- * Always use 0 to simplify comparison.
- */
- if ((freq->div_src_val & CFG_RCGR_DIV_MASK) == 1)
- freq->div_src_val &= ~CFG_RCGR_DIV_MASK;
- cfg = readl_relaxed(CFG_RCGR_REG(rcg));
- if ((cfg & CFG_RCGR_DIV_MASK) == 1)
- cfg &= ~CFG_RCGR_DIV_MASK;
- if (cfg != freq->div_src_val)
- return false;
-
- return true;
-}
-
static int set_rate_edp_pixel(struct clk *clk, unsigned long rate)
{
struct rcg_clk *rcg = to_rcg_clk(clk);
@@ -1404,8 +1376,7 @@ static int set_rate_edp_pixel(struct clk *clk, unsigned long rate)
pixel_freq->d_val = ~frac->den;
}
spin_lock_irqsave(&local_clock_reg_lock, flags);
- if (!is_same_rcg_config(rcg, pixel_freq, true))
- __set_rate_mnd(rcg, pixel_freq);
+ __set_rate_mnd(rcg, pixel_freq);
spin_unlock_irqrestore(&local_clock_reg_lock, flags);
return 0;
}
@@ -1466,8 +1437,7 @@ static int set_rate_byte(struct clk *clk, unsigned long rate)
byte_freq->div_src_val |= BVAL(4, 0, div);
spin_lock_irqsave(&local_clock_reg_lock, flags);
- if (!is_same_rcg_config(rcg, byte_freq, false))
- __set_rate_hid(rcg, byte_freq);
+ __set_rate_hid(rcg, byte_freq);
spin_unlock_irqrestore(&local_clock_reg_lock, flags);
return 0;
@@ -1788,8 +1758,7 @@ static int rcg_clk_set_rate_dp(struct clk *clk, unsigned long rate)
}
spin_lock_irqsave(&local_clock_reg_lock, flags);
- if (!is_same_rcg_config(rcg, freq_tbl, true))
- __set_rate_mnd(rcg, freq_tbl);
+ __set_rate_mnd(rcg, freq_tbl);
spin_unlock_irqrestore(&local_clock_reg_lock, flags);
return 0;
}
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index 9d9aa61c480a..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);
@@ -1541,8 +1629,8 @@ static int clk_osm_setup_hw_table(struct clk_osm *c)
{
struct osm_entry *entry = c->osm_table;
int i;
- u32 freq_val, volt_val, override_val, spare_val;
- u32 table_entry_offset, last_spare, last_virtual_corner = 0;
+ u32 freq_val = 0, volt_val = 0, override_val = 0, spare_val = 0;
+ u32 table_entry_offset = 0, last_spare = 0, last_virtual_corner = 0;
for (i = 0; i < OSM_TABLE_SIZE; i++) {
if (i < c->num_entries) {
@@ -2758,7 +2846,7 @@ static ssize_t debugfs_trace_method_get(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct clk_osm *c = file->private_data;
- int len, rc;
+ int len = 0, rc;
if (IS_ERR(file) || file == NULL) {
pr_err("input error %ld\n", PTR_ERR(file));
@@ -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/msm/mdss/mdss-hdmi-pll-8998.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c
index c60c4864442f..c4215f30acce 100644
--- a/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c
+++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,10 @@
#define _W(x, y, z) MDSS_PLL_REG_W(x, y, z)
#define _R(x, y) MDSS_PLL_REG_R(x, y)
+/* CONSTANTS */
+#define HDMI_VERSION_8998_3_3 1
+#define HDMI_VERSION_8998_1_8 2
+
/* PLL REGISTERS */
#define FREQ_UPDATE (0x008)
#define BIAS_EN_CLKBUFLR_EN (0x034)
@@ -277,7 +281,7 @@ find_optimal_index:
}
static int hdmi_8998_config_phy(unsigned long rate,
- struct hdmi_8998_reg_cfg *cfg)
+ struct hdmi_8998_reg_cfg *cfg, u32 ver)
{
u64 const high_freq_bit_clk_threshold = 3400000000UL;
u64 const dig_freq_bit_clk_threshold = 1500000000UL;
@@ -359,6 +363,7 @@ static int hdmi_8998_config_phy(unsigned long rate,
pr_debug("INTEGLOOP_GAIN = %llu\n", integloop_gain);
pr_debug("CMP_RNG = %llu\n", cmp_rng);
pr_debug("PLL_CMP = %llu\n", pll_cmp);
+ pr_debug("VER=%d\n", ver);
cfg->svs_mode_clk_sel = (digclk_divsel & 0xFF);
cfg->hsclk_sel = (0x20 | hsclk_sel);
@@ -382,82 +387,105 @@ static int hdmi_8998_config_phy(unsigned long rate,
cfg->core_clk_en = 0x2C;
cfg->coreclk_div_mode0 = 0x5;
cfg->phy_mode = (tmds_bclk_ratio ? 0x5 : 0x4);
+ /* V1P8_SEL */
+ if (ver == HDMI_VERSION_8998_1_8)
+ cfg->phy_mode |= 1 << 4;
cfg->ssc_en_center = 0x0;
- if (bclk > high_freq_bit_clk_threshold) {
- cfg->l0_tx_drv_lvl = 0xA;
- cfg->l0_tx_emp_post1_lvl = 0x3;
- cfg->l1_tx_drv_lvl = 0xA;
- cfg->l1_tx_emp_post1_lvl = 0x3;
- cfg->l2_tx_drv_lvl = 0xA;
- cfg->l2_tx_emp_post1_lvl = 0x3;
- cfg->l3_tx_drv_lvl = 0x8;
- cfg->l3_tx_emp_post1_lvl = 0x3;
- cfg->l0_pre_driver_1 = 0x0;
- cfg->l0_pre_driver_2 = 0x1C;
- cfg->l1_pre_driver_1 = 0x0;
- cfg->l1_pre_driver_2 = 0x1C;
- cfg->l2_pre_driver_1 = 0x0;
- cfg->l2_pre_driver_2 = 0x1C;
- cfg->l3_pre_driver_1 = 0x0;
- cfg->l3_pre_driver_2 = 0x0;
- } else if (bclk > dig_freq_bit_clk_threshold) {
- cfg->l0_tx_drv_lvl = 0x9;
- cfg->l0_tx_emp_post1_lvl = 0x3;
- cfg->l1_tx_drv_lvl = 0x9;
- cfg->l1_tx_emp_post1_lvl = 0x3;
- cfg->l2_tx_drv_lvl = 0x9;
- cfg->l2_tx_emp_post1_lvl = 0x3;
- cfg->l3_tx_drv_lvl = 0x8;
- cfg->l3_tx_emp_post1_lvl = 0x3;
- cfg->l0_pre_driver_1 = 0x0;
- cfg->l0_pre_driver_2 = 0x16;
- cfg->l1_pre_driver_1 = 0x0;
- cfg->l1_pre_driver_2 = 0x16;
- cfg->l2_pre_driver_1 = 0x0;
- cfg->l2_pre_driver_2 = 0x16;
- cfg->l3_pre_driver_1 = 0x0;
- cfg->l3_pre_driver_2 = 0x0;
- } else if (bclk > mid_freq_bit_clk_threshold) {
- cfg->l0_tx_drv_lvl = 0x9;
- cfg->l0_tx_emp_post1_lvl = 0x3;
- cfg->l1_tx_drv_lvl = 0x9;
- cfg->l1_tx_emp_post1_lvl = 0x3;
- cfg->l2_tx_drv_lvl = 0x9;
- cfg->l2_tx_emp_post1_lvl = 0x3;
- cfg->l3_tx_drv_lvl = 0x8;
- cfg->l3_tx_emp_post1_lvl = 0x3;
- cfg->l0_pre_driver_1 = 0x0;
- cfg->l0_pre_driver_2 = 0x0E;
- cfg->l1_pre_driver_1 = 0x0;
- cfg->l1_pre_driver_2 = 0x0E;
- cfg->l2_pre_driver_1 = 0x0;
- cfg->l2_pre_driver_2 = 0x0E;
- cfg->l3_pre_driver_1 = 0x0;
- cfg->l3_pre_driver_2 = 0x0;
+ if (ver == HDMI_VERSION_8998_3_3) {
+ if (bclk > high_freq_bit_clk_threshold) {
+ cfg->l0_tx_drv_lvl = 0xA;
+ cfg->l0_tx_emp_post1_lvl = 0x3;
+ cfg->l1_tx_drv_lvl = 0xA;
+ cfg->l1_tx_emp_post1_lvl = 0x3;
+ cfg->l2_tx_drv_lvl = 0xA;
+ cfg->l2_tx_emp_post1_lvl = 0x3;
+ cfg->l3_tx_drv_lvl = 0x8;
+ cfg->l3_tx_emp_post1_lvl = 0x3;
+ cfg->l0_pre_driver_1 = 0x0;
+ cfg->l0_pre_driver_2 = 0x1C;
+ cfg->l1_pre_driver_1 = 0x0;
+ cfg->l1_pre_driver_2 = 0x1C;
+ cfg->l2_pre_driver_1 = 0x0;
+ cfg->l2_pre_driver_2 = 0x1C;
+ cfg->l3_pre_driver_1 = 0x0;
+ cfg->l3_pre_driver_2 = 0x0;
+ } else if (bclk > dig_freq_bit_clk_threshold) {
+ cfg->l0_tx_drv_lvl = 0x9;
+ cfg->l0_tx_emp_post1_lvl = 0x3;
+ cfg->l1_tx_drv_lvl = 0x9;
+ cfg->l1_tx_emp_post1_lvl = 0x3;
+ cfg->l2_tx_drv_lvl = 0x9;
+ cfg->l2_tx_emp_post1_lvl = 0x3;
+ cfg->l3_tx_drv_lvl = 0x8;
+ cfg->l3_tx_emp_post1_lvl = 0x3;
+ cfg->l0_pre_driver_1 = 0x0;
+ cfg->l0_pre_driver_2 = 0x16;
+ cfg->l1_pre_driver_1 = 0x0;
+ cfg->l1_pre_driver_2 = 0x16;
+ cfg->l2_pre_driver_1 = 0x0;
+ cfg->l2_pre_driver_2 = 0x16;
+ cfg->l3_pre_driver_1 = 0x0;
+ cfg->l3_pre_driver_2 = 0x0;
+ } else if (bclk > mid_freq_bit_clk_threshold) {
+ cfg->l0_tx_drv_lvl = 0x9;
+ cfg->l0_tx_emp_post1_lvl = 0x3;
+ cfg->l1_tx_drv_lvl = 0x9;
+ cfg->l1_tx_emp_post1_lvl = 0x3;
+ cfg->l2_tx_drv_lvl = 0x9;
+ cfg->l2_tx_emp_post1_lvl = 0x3;
+ cfg->l3_tx_drv_lvl = 0x8;
+ cfg->l3_tx_emp_post1_lvl = 0x3;
+ cfg->l0_pre_driver_1 = 0x0;
+ cfg->l0_pre_driver_2 = 0x0E;
+ cfg->l1_pre_driver_1 = 0x0;
+ cfg->l1_pre_driver_2 = 0x0E;
+ cfg->l2_pre_driver_1 = 0x0;
+ cfg->l2_pre_driver_2 = 0x0E;
+ cfg->l3_pre_driver_1 = 0x0;
+ cfg->l3_pre_driver_2 = 0x0;
+ } else {
+ cfg->l0_tx_drv_lvl = 0x0;
+ cfg->l0_tx_emp_post1_lvl = 0x0;
+ cfg->l1_tx_drv_lvl = 0x0;
+ cfg->l1_tx_emp_post1_lvl = 0x0;
+ cfg->l2_tx_drv_lvl = 0x0;
+ cfg->l2_tx_emp_post1_lvl = 0x0;
+ cfg->l3_tx_drv_lvl = 0x0;
+ cfg->l3_tx_emp_post1_lvl = 0x0;
+ cfg->l0_pre_driver_1 = 0x0;
+ cfg->l0_pre_driver_2 = 0x01;
+ cfg->l1_pre_driver_1 = 0x0;
+ cfg->l1_pre_driver_2 = 0x01;
+ cfg->l2_pre_driver_1 = 0x0;
+ cfg->l2_pre_driver_2 = 0x01;
+ cfg->l3_pre_driver_1 = 0x0;
+ cfg->l3_pre_driver_2 = 0x0;
+ }
} else {
- cfg->l0_tx_drv_lvl = 0x0;
- cfg->l0_tx_emp_post1_lvl = 0x0;
- cfg->l1_tx_drv_lvl = 0x0;
- cfg->l1_tx_emp_post1_lvl = 0x0;
- cfg->l2_tx_drv_lvl = 0x0;
- cfg->l2_tx_emp_post1_lvl = 0x0;
- cfg->l3_tx_drv_lvl = 0x0;
+ cfg->l0_tx_drv_lvl = 0xF;
+ cfg->l0_tx_emp_post1_lvl = 0x5;
+ cfg->l1_tx_drv_lvl = 0xF;
+ cfg->l1_tx_emp_post1_lvl = 0x2;
+ cfg->l2_tx_drv_lvl = 0xF;
+ cfg->l2_tx_emp_post1_lvl = 0x2;
+ cfg->l3_tx_drv_lvl = 0xF;
cfg->l3_tx_emp_post1_lvl = 0x0;
cfg->l0_pre_driver_1 = 0x0;
- cfg->l0_pre_driver_2 = 0x01;
+ cfg->l0_pre_driver_2 = 0x1E;
cfg->l1_pre_driver_1 = 0x0;
- cfg->l1_pre_driver_2 = 0x01;
+ cfg->l1_pre_driver_2 = 0x1E;
cfg->l2_pre_driver_1 = 0x0;
- cfg->l2_pre_driver_2 = 0x01;
+ cfg->l2_pre_driver_2 = 0x1E;
cfg->l3_pre_driver_1 = 0x0;
- cfg->l3_pre_driver_2 = 0x0;
+ cfg->l3_pre_driver_2 = 0x10;
}
return rc;
}
-static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate)
+static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate,
+ u32 ver)
{
int rc = 0;
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
@@ -465,7 +493,7 @@ static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate)
struct hdmi_8998_reg_cfg cfg = {0};
void __iomem *phy = io->phy_base, *pll = io->pll_base;
- rc = hdmi_8998_config_phy(rate, &cfg);
+ rc = hdmi_8998_config_phy(rate, &cfg, ver);
if (rc) {
pr_err("rate calculation failed\n, rc=%d", rc);
return rc;
@@ -699,7 +727,7 @@ static int hdmi_8998_vco_get_lock_range(struct clk *c,
}
static int hdmi_8998_vco_rate_atomic_update(struct clk *c,
- unsigned long rate)
+ unsigned long rate, u32 ver)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
@@ -707,7 +735,7 @@ static int hdmi_8998_vco_rate_atomic_update(struct clk *c,
struct hdmi_8998_reg_cfg cfg = {0};
int rc = 0;
- rc = hdmi_8998_config_phy(rate, &cfg);
+ rc = hdmi_8998_config_phy(rate, &cfg, ver);
if (rc) {
pr_err("rate calculation failed\n, rc=%d", rc);
goto end;
@@ -728,7 +756,7 @@ end:
return rc;
}
-static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate)
+static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate, u32 ver)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
@@ -767,9 +795,9 @@ static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate)
set_power_dwn = 1;
if (atomic_update)
- rc = hdmi_8998_vco_rate_atomic_update(c, rate);
+ rc = hdmi_8998_vco_rate_atomic_update(c, rate, ver);
else
- rc = hdmi_8998_pll_set_clk_rate(c, rate);
+ rc = hdmi_8998_pll_set_clk_rate(c, rate, ver);
if (rc) {
pr_err("failed to set clk rate\n");
@@ -806,7 +834,7 @@ static long hdmi_8998_vco_round_rate(struct clk *c, unsigned long rate)
return rrate;
}
-static int hdmi_8998_vco_prepare(struct clk *c)
+static int hdmi_8998_vco_prepare(struct clk *c, u32 ver)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
@@ -824,7 +852,7 @@ static int hdmi_8998_vco_prepare(struct clk *c)
}
if (!vco->rate_set && vco->rate) {
- rc = hdmi_8998_pll_set_clk_rate(c, vco->rate);
+ rc = hdmi_8998_pll_set_clk_rate(c, vco->rate, ver);
if (rc) {
pr_err("set rate failed, rc=%d\n", rc);
goto error;
@@ -902,10 +930,38 @@ static enum handoff hdmi_8998_vco_handoff(struct clk *c)
return ret;
}
-static struct clk_ops hdmi_8998_vco_clk_ops = {
- .set_rate = hdmi_8998_vco_set_rate,
+static int hdmi_8998_3p3_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8998_vco_set_rate(c, rate, HDMI_VERSION_8998_3_3);
+}
+
+static int hdmi_8998_1p8_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8998_vco_set_rate(c, rate, HDMI_VERSION_8998_1_8);
+}
+
+static int hdmi_8998_3p3_vco_prepare(struct clk *c)
+{
+ return hdmi_8998_vco_prepare(c, HDMI_VERSION_8998_3_3);
+}
+
+static int hdmi_8998_1p8_vco_prepare(struct clk *c)
+{
+ return hdmi_8998_vco_prepare(c, HDMI_VERSION_8998_1_8);
+}
+
+static struct clk_ops hdmi_8998_3p3_vco_clk_ops = {
+ .set_rate = hdmi_8998_3p3_vco_set_rate,
+ .round_rate = hdmi_8998_vco_round_rate,
+ .prepare = hdmi_8998_3p3_vco_prepare,
+ .unprepare = hdmi_8998_vco_unprepare,
+ .handoff = hdmi_8998_vco_handoff,
+};
+
+static struct clk_ops hdmi_8998_1p8_vco_clk_ops = {
+ .set_rate = hdmi_8998_1p8_vco_set_rate,
.round_rate = hdmi_8998_vco_round_rate,
- .prepare = hdmi_8998_vco_prepare,
+ .prepare = hdmi_8998_1p8_vco_prepare,
.unprepare = hdmi_8998_vco_unprepare,
.handoff = hdmi_8998_vco_handoff,
};
@@ -915,7 +971,7 @@ static struct hdmi_pll_vco_clk hdmi_vco_clk = {
.max_rate = HDMI_VCO_MAX_RATE_HZ,
.c = {
.dbg_name = "hdmi_8998_vco_clk",
- .ops = &hdmi_8998_vco_clk_ops,
+ .ops = &hdmi_8998_3p3_vco_clk_ops,
CLK_INIT(hdmi_vco_clk.c),
},
};
@@ -925,7 +981,7 @@ static struct clk_lookup hdmipllcc_8998[] = {
};
int hdmi_8998_pll_clock_register(struct platform_device *pdev,
- struct mdss_pll_resources *pll_res)
+ struct mdss_pll_resources *pll_res, u32 ver)
{
int rc = 0;
@@ -936,8 +992,20 @@ int hdmi_8998_pll_clock_register(struct platform_device *pdev,
hdmi_vco_clk.priv = pll_res;
+ switch (ver) {
+ case HDMI_VERSION_8998_3_3:
+ hdmi_vco_clk.c.ops = &hdmi_8998_3p3_vco_clk_ops;
+ break;
+ case HDMI_VERSION_8998_1_8:
+ hdmi_vco_clk.c.ops = &hdmi_8998_1p8_vco_clk_ops;
+ break;
+ default:
+ hdmi_vco_clk.c.ops = &hdmi_8998_3p3_vco_clk_ops;
+ break;
+ };
+
rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8998,
- ARRAY_SIZE(hdmipllcc_8998));
+ ARRAY_SIZE(hdmipllcc_8998));
if (rc) {
pr_err("clock register failed, rc=%d\n", rc);
return rc;
@@ -945,3 +1013,17 @@ int hdmi_8998_pll_clock_register(struct platform_device *pdev,
return rc;
}
+
+int hdmi_8998_3p3_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8998_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8998_3_3);
+}
+
+int hdmi_8998_1p8_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8998_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8998_1_8);
+}
diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll.h b/drivers/clk/msm/mdss/mdss-hdmi-pll.h
index 19f9b925644a..9e6a39481286 100644
--- a/drivers/clk/msm/mdss/mdss-hdmi-pll.h
+++ b/drivers/clk/msm/mdss/mdss-hdmi-pll.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
@@ -45,17 +45,19 @@ int hdmi_20nm_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev,
- struct mdss_pll_resources *pll_res);
+ struct mdss_pll_resources *pll_res);
int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev,
- struct mdss_pll_resources *pll_res);
+ struct mdss_pll_resources *pll_res);
int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev,
- struct mdss_pll_resources *pll_res);
+ struct mdss_pll_resources *pll_res);
int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev,
- struct mdss_pll_resources *pll_res);
+ struct mdss_pll_resources *pll_res);
-int hdmi_8998_pll_clock_register(struct platform_device *pdev,
- struct mdss_pll_resources *pll_res);
+int hdmi_8998_3p3_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int hdmi_8998_1p8_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
#endif
diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c
index 01ce2b1817f2..b5c98774ba92 100644
--- a/drivers/clk/msm/mdss/mdss-pll.c
+++ b/drivers/clk/msm/mdss/mdss-pll.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -149,7 +149,9 @@ static int mdss_pll_resource_parse(struct platform_device *pdev,
"qcom,mdss_hdmi_pll_8996_v3_1p8")) {
pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3_1_8;
} else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8998")) {
- pll_res->pll_interface_type = MDSS_HDMI_PLL_8998;
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8998_3_3;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8998_1p8")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8998_1_8;
} else {
goto err;
}
@@ -193,8 +195,11 @@ static int mdss_pll_clock_register(struct platform_device *pdev,
case MDSS_HDMI_PLL_8996_V3_1_8:
rc = hdmi_8996_v3_1p8_pll_clock_register(pdev, pll_res);
break;
- case MDSS_HDMI_PLL_8998:
- rc = hdmi_8998_pll_clock_register(pdev, pll_res);
+ case MDSS_HDMI_PLL_8998_3_3:
+ rc = hdmi_8998_3p3_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8998_1_8:
+ rc = hdmi_8998_1p8_pll_clock_register(pdev, pll_res);
break;
case MDSS_UNKNOWN_PLL:
default:
@@ -401,6 +406,7 @@ static const struct of_device_id mdss_pll_dt_match[] = {
{.compatible = "qcom,mdss_hdmi_pll_8996_v3_1p8"},
{.compatible = "qcom,mdss_dp_pll_8998"},
{.compatible = "qcom,mdss_hdmi_pll_8998"},
+ {.compatible = "qcom,mdss_hdmi_pll_8998_1p8"},
{}
};
diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h
index 8fffaf30d4ec..0120d71f0daf 100644
--- a/drivers/clk/msm/mdss/mdss-pll.h
+++ b/drivers/clk/msm/mdss/mdss-pll.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -37,7 +37,8 @@ enum {
MDSS_HDMI_PLL_8996_V2,
MDSS_HDMI_PLL_8996_V3,
MDSS_HDMI_PLL_8996_V3_1_8,
- MDSS_HDMI_PLL_8998,
+ MDSS_HDMI_PLL_8998_3_3,
+ MDSS_HDMI_PLL_8998_1_8,
MDSS_UNKNOWN_PLL,
};
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-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 8bf45f572c5e..d99e13817a29 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -719,9 +719,22 @@ static int clk_osm_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
+static int clk_osm_acd_init(struct clk_osm *c);
+
static int clk_osm_enable(struct clk_hw *hw)
{
struct clk_osm *cpuclk = to_clk_osm(hw);
+ 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);
@@ -3272,17 +3285,6 @@ static int clk_cpu_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/clk-regmap-mux-div.c b/drivers/clk/qcom/clk-regmap-mux-div.c
new file mode 100644
index 000000000000..942a68e2a650
--- /dev/null
+++ b/drivers/clk/qcom/clk-regmap-mux-div.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2014, 2017, 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;
+}
+
+int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
+ u32 *div)
+{
+ int ret = 0;
+ u32 val, __div, __src;
+ const char *name = clk_hw_get_name(&md->clkr.hw);
+
+ ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
+ if (ret)
+ return ret;
+
+ if (val & CMD_RCGR_DIRTY_CFG) {
+ pr_err("%s: RCG configuration is pending\n", name);
+ return -EBUSY;
+ }
+
+ ret = regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
+ if (ret)
+ return ret;
+
+ __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;
+
+ return ret;
+}
+
+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..63a696a96033
--- /dev/null
+++ b/drivers/clk/qcom/clk-regmap-mux-div.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2014, 2017, 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);
+int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, u32 *div);
+
+#endif
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 3b2f46bacd77..1dfd1765319b 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -120,6 +120,15 @@ config CPU_FREQ_DEFAULT_GOV_SCHED
cpu frequency using CPU utilization estimates from the
scheduler.
+config CPU_FREQ_DEFAULT_GOV_SCHEDUTIL
+ bool "schedutil"
+ depends on SMP
+ select CPU_FREQ_GOV_SCHEDUTIL
+ select CPU_FREQ_GOV_PERFORMANCE
+ help
+ Use the 'schedutil' CPUFreq governor by default. If unsure,
+ have a look at the help section of that governor. The fallback
+ governor will be 'performance'.
endchoice
config CPU_FREQ_GOV_PERFORMANCE
@@ -239,6 +248,23 @@ config CPU_FREQ_GOV_SCHED
If in doubt, say N.
+config CPU_FREQ_GOV_SCHEDUTIL
+ bool "'schedutil' cpufreq policy governor"
+ depends on CPU_FREQ && SMP
+ select CPU_FREQ_GOV_ATTR_SET
+ select IRQ_WORK
+ help
+ This governor makes decisions based on the utilization data provided
+ by the scheduler. It sets the CPU frequency to be proportional to
+ the utilization/capacity ratio coming from the scheduler. If the
+ utilization is frequency-invariant, the new frequency is also
+ proportional to the maximum available frequency. If that is not the
+ case, it is proportional to the current frequency of the CPU. The
+ frequency tipping point is at utilization/capacity equal to 80% in
+ both cases.
+
+ If in doubt, say N.
+
comment "CPU frequency scaling drivers"
config CPUFREQ_DT
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 4a2f914e0752..6d4a7aeb506d 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -1,5 +1,5 @@
# CPUfreq core
-obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o
+obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o cpufreq_governor_attr_set.o
# CPUfreq stats
obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index a0dba9beac05..5f0f983ce173 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -541,6 +541,38 @@ void cpufreq_freq_transition_end(struct cpufreq_policy *policy,
}
EXPORT_SYMBOL_GPL(cpufreq_freq_transition_end);
+/**
+ * cpufreq_driver_resolve_freq - Map a target frequency to a driver-supported
+ * one.
+ * @target_freq: target frequency to resolve.
+ *
+ * The target to driver frequency mapping is cached in the policy.
+ *
+ * Return: Lowest driver-supported frequency greater than or equal to the
+ * given target_freq, subject to policy (min/max) and driver limitations.
+ */
+unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy,
+ unsigned int target_freq)
+{
+ target_freq = clamp_val(target_freq, policy->min, policy->max);
+ policy->cached_target_freq = target_freq;
+
+ if (cpufreq_driver->target_index) {
+ int idx, rv;
+
+ rv = cpufreq_frequency_table_target(policy, policy->freq_table,
+ target_freq,
+ CPUFREQ_RELATION_L,
+ &idx);
+ if (rv)
+ return target_freq;
+ policy->cached_resolved_idx = idx;
+ return policy->freq_table[idx].frequency;
+ }
+
+ return target_freq;
+}
+EXPORT_SYMBOL_GPL(cpufreq_driver_resolve_freq);
/*********************************************************************
* SYSFS INTERFACE *
@@ -2536,6 +2568,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
if (!(cpufreq_driver->flags & CPUFREQ_STICKY) &&
list_empty(&cpufreq_policy_list)) {
/* if all ->init() calls failed, unregister */
+ ret = -ENODEV;
pr_debug("%s: No CPU initialized for driver %s\n", __func__,
driver_data->name);
goto err_if_unreg;
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 1fa1deb6e91f..c395f9198fd2 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -212,8 +212,8 @@ static ssize_t store_down_threshold(struct dbs_data *dbs_data, const char *buf,
int ret;
ret = sscanf(buf, "%u", &input);
- /* cannot be lower than 11 otherwise freq will not fall */
- if (ret != 1 || input < 11 || input > 100 ||
+ /* cannot be lower than 1 otherwise freq will not fall */
+ if (ret != 1 || input < 1 || input > 100 ||
input >= cs_tuners->up_threshold)
return -EINVAL;
diff --git a/drivers/cpufreq/cpufreq_governor_attr_set.c b/drivers/cpufreq/cpufreq_governor_attr_set.c
new file mode 100644
index 000000000000..52841f807a7e
--- /dev/null
+++ b/drivers/cpufreq/cpufreq_governor_attr_set.c
@@ -0,0 +1,84 @@
+/*
+ * Abstract code for CPUFreq governor tunable sysfs attributes.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "cpufreq_governor.h"
+
+static inline struct gov_attr_set *to_gov_attr_set(struct kobject *kobj)
+{
+ return container_of(kobj, struct gov_attr_set, kobj);
+}
+
+static inline struct governor_attr *to_gov_attr(struct attribute *attr)
+{
+ return container_of(attr, struct governor_attr, attr);
+}
+
+static ssize_t governor_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct governor_attr *gattr = to_gov_attr(attr);
+
+ return gattr->show(to_gov_attr_set(kobj), buf);
+}
+
+static ssize_t governor_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gov_attr_set *attr_set = to_gov_attr_set(kobj);
+ struct governor_attr *gattr = to_gov_attr(attr);
+ int ret;
+
+ mutex_lock(&attr_set->update_lock);
+ ret = attr_set->usage_count ? gattr->store(attr_set, buf, count) : -EBUSY;
+ mutex_unlock(&attr_set->update_lock);
+ return ret;
+}
+
+const struct sysfs_ops governor_sysfs_ops = {
+ .show = governor_show,
+ .store = governor_store,
+};
+EXPORT_SYMBOL_GPL(governor_sysfs_ops);
+
+void gov_attr_set_init(struct gov_attr_set *attr_set, struct list_head *list_node)
+{
+ INIT_LIST_HEAD(&attr_set->policy_list);
+ mutex_init(&attr_set->update_lock);
+ attr_set->usage_count = 1;
+ list_add(list_node, &attr_set->policy_list);
+}
+EXPORT_SYMBOL_GPL(gov_attr_set_init);
+
+void gov_attr_set_get(struct gov_attr_set *attr_set, struct list_head *list_node)
+{
+ mutex_lock(&attr_set->update_lock);
+ attr_set->usage_count++;
+ list_add(list_node, &attr_set->policy_list);
+ mutex_unlock(&attr_set->update_lock);
+}
+EXPORT_SYMBOL_GPL(gov_attr_set_get);
+
+unsigned int gov_attr_set_put(struct gov_attr_set *attr_set, struct list_head *list_node)
+{
+ unsigned int count;
+
+ mutex_lock(&attr_set->update_lock);
+ list_del(list_node);
+ count = --attr_set->usage_count;
+ mutex_unlock(&attr_set->update_lock);
+ if (count)
+ return count;
+
+ kobject_put(&attr_set->kobj);
+ mutex_destroy(&attr_set->update_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gov_attr_set_put);
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index b69e59eeeae1..584a1857624a 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -694,22 +694,21 @@ static int cpu_power_select(struct cpuidle_device *dev,
int best_level = -1;
uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY,
dev->cpu);
- uint32_t sleep_us =
- (uint32_t)(ktime_to_us(tick_nohz_get_sleep_length()));
+ s64 sleep_us = ktime_to_us(tick_nohz_get_sleep_length());
uint32_t modified_time_us = 0;
uint32_t next_event_us = 0;
int i, idx_restrict;
uint32_t lvl_latency_us = 0;
uint64_t predicted = 0;
uint32_t htime = 0, idx_restrict_time = 0;
- uint32_t next_wakeup_us = sleep_us;
+ uint32_t next_wakeup_us = (uint32_t)sleep_us;
uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu);
uint32_t *max_residency = get_per_cpu_max_residency(dev->cpu);
if (!cpu)
return -EINVAL;
- if (sleep_disabled && !cpu_isolated(dev->cpu))
+ if ((sleep_disabled && !cpu_isolated(dev->cpu)) || sleep_us < 0)
return 0;
idx_restrict = cpu->nlevels + 1;
@@ -750,8 +749,8 @@ static int cpu_power_select(struct cpuidle_device *dev,
if (next_wakeup_us > max_residency[i]) {
predicted = lpm_cpuidle_predict(dev, cpu,
&idx_restrict, &idx_restrict_time);
- if (predicted < min_residency[i])
- predicted = 0;
+ if (predicted && (predicted < min_residency[i]))
+ predicted = min_residency[i];
} else
invalidate_predict_history(dev);
}
@@ -1119,10 +1118,14 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
bool from_idle, int predicted)
{
struct lpm_cluster_level *level = &cluster->levels[idx];
+ struct cpumask online_cpus;
int ret, i;
+ cpumask_and(&online_cpus, &cluster->num_children_in_sync,
+ cpu_online_mask);
+
if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus)
- || is_IPI_pending(&cluster->num_children_in_sync)) {
+ || is_IPI_pending(&online_cpus)) {
return -EPERM;
}
diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c
index 4002a5b57250..f99a421c0ee4 100644
--- a/drivers/crypto/msm/ice.c
+++ b/drivers/crypto/msm/ice.c
@@ -125,6 +125,9 @@ static int qti_ice_setting_config(struct request *req,
return -EPERM;
}
+ if (!setting)
+ return -EINVAL;
+
if ((short)(crypto_data->key_index) >= 0) {
memcpy(&setting->crypto_data, crypto_data,
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/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 57ff46284f15..c97336a2ba92 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -325,6 +325,8 @@ static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
| M2P_CONTROL_ENABLE;
m2p_set_control(edmac, control);
+ edmac->buffer = 0;
+
return 0;
}
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index b1bc945f008f..56410ea75ac5 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -117,7 +117,7 @@ struct usb_dmac {
#define USB_DMASWR 0x0008
#define USB_DMASWR_SWR (1 << 0)
#define USB_DMAOR 0x0060
-#define USB_DMAOR_AE (1 << 2)
+#define USB_DMAOR_AE (1 << 1)
#define USB_DMAOR_DME (1 << 0)
#define USB_DMASAR 0x0000
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 16a7b6816744..8decab2a9cce 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -597,7 +597,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
int idx, i;
for (i = 0, idx = 0; idx <= index; i++) {
- struct acpi_gpio_info info;
+ struct acpi_gpio_info info = {0, 0};
struct gpio_desc *desc;
desc = acpi_get_gpiod_by_index(adev, NULL, i, &info);
diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c
index 57a2e347f04d..0f0094b58d1f 100644
--- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c
@@ -893,6 +893,12 @@ static bool ci_dpm_vblank_too_short(struct amdgpu_device *adev)
u32 vblank_time = amdgpu_dpm_get_vblank_time(adev);
u32 switch_limit = adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 300;
+ /* disable mclk switching if the refresh is >120Hz, even if the
+ * blanking period would allow it
+ */
+ if (amdgpu_dpm_get_vrefresh(adev) > 120)
+ return true;
+
if (vblank_time < switch_limit)
return true;
else
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/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index 05f6522c0457..b92139e9b9d8 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -113,6 +113,7 @@ struct ast_private {
struct ttm_bo_kmap_obj cache_kmap;
int next_cursor;
bool support_wide_screen;
+ bool DisableP2A;
enum ast_tx_chip tx_chip_type;
u8 dp501_maxclk;
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index 9b8f0b975ca6..6c021165ca67 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -124,6 +124,12 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
} else
*need_post = false;
+ /* Check P2A Access */
+ ast->DisableP2A = true;
+ data = ast_read32(ast, 0xf004);
+ if (data != 0xFFFFFFFF)
+ ast->DisableP2A = false;
+
/* Check if we support wide screen */
switch (ast->chip) {
case AST1180:
@@ -140,15 +146,17 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
ast->support_wide_screen = true;
else {
ast->support_wide_screen = false;
- /* Read SCU7c (silicon revision register) */
- ast_write32(ast, 0xf004, 0x1e6e0000);
- ast_write32(ast, 0xf000, 0x1);
- data = ast_read32(ast, 0x1207c);
- data &= 0x300;
- if (ast->chip == AST2300 && data == 0x0) /* ast1300 */
- ast->support_wide_screen = true;
- if (ast->chip == AST2400 && data == 0x100) /* ast1400 */
- ast->support_wide_screen = true;
+ if (ast->DisableP2A == false) {
+ /* Read SCU7c (silicon revision register) */
+ ast_write32(ast, 0xf004, 0x1e6e0000);
+ ast_write32(ast, 0xf000, 0x1);
+ data = ast_read32(ast, 0x1207c);
+ data &= 0x300;
+ if (ast->chip == AST2300 && data == 0x0) /* ast1300 */
+ ast->support_wide_screen = true;
+ if (ast->chip == AST2400 && data == 0x100) /* ast1400 */
+ ast->support_wide_screen = true;
+ }
}
break;
}
@@ -216,80 +224,81 @@ static int ast_get_dram_info(struct drm_device *dev)
uint32_t data, data2;
uint32_t denum, num, div, ref_pll;
- ast_write32(ast, 0xf004, 0x1e6e0000);
- ast_write32(ast, 0xf000, 0x1);
-
-
- ast_write32(ast, 0x10000, 0xfc600309);
-
- do {
- if (pci_channel_offline(dev->pdev))
- return -EIO;
- } while (ast_read32(ast, 0x10000) != 0x01);
- data = ast_read32(ast, 0x10004);
-
- if (data & 0x40)
+ if (ast->DisableP2A)
+ {
ast->dram_bus_width = 16;
+ ast->dram_type = AST_DRAM_1Gx16;
+ ast->mclk = 396;
+ }
else
- ast->dram_bus_width = 32;
+ {
+ ast_write32(ast, 0xf004, 0x1e6e0000);
+ ast_write32(ast, 0xf000, 0x1);
+ data = ast_read32(ast, 0x10004);
+
+ if (data & 0x40)
+ ast->dram_bus_width = 16;
+ else
+ ast->dram_bus_width = 32;
+
+ if (ast->chip == AST2300 || ast->chip == AST2400) {
+ switch (data & 0x03) {
+ case 0:
+ ast->dram_type = AST_DRAM_512Mx16;
+ break;
+ default:
+ case 1:
+ ast->dram_type = AST_DRAM_1Gx16;
+ break;
+ case 2:
+ ast->dram_type = AST_DRAM_2Gx16;
+ break;
+ case 3:
+ ast->dram_type = AST_DRAM_4Gx16;
+ break;
+ }
+ } else {
+ switch (data & 0x0c) {
+ case 0:
+ case 4:
+ ast->dram_type = AST_DRAM_512Mx16;
+ break;
+ case 8:
+ if (data & 0x40)
+ ast->dram_type = AST_DRAM_1Gx16;
+ else
+ ast->dram_type = AST_DRAM_512Mx32;
+ break;
+ case 0xc:
+ ast->dram_type = AST_DRAM_1Gx32;
+ break;
+ }
+ }
- if (ast->chip == AST2300 || ast->chip == AST2400) {
- switch (data & 0x03) {
- case 0:
- ast->dram_type = AST_DRAM_512Mx16;
- break;
- default:
- case 1:
- ast->dram_type = AST_DRAM_1Gx16;
- break;
- case 2:
- ast->dram_type = AST_DRAM_2Gx16;
- break;
+ data = ast_read32(ast, 0x10120);
+ data2 = ast_read32(ast, 0x10170);
+ if (data2 & 0x2000)
+ ref_pll = 14318;
+ else
+ ref_pll = 12000;
+
+ denum = data & 0x1f;
+ num = (data & 0x3fe0) >> 5;
+ data = (data & 0xc000) >> 14;
+ switch (data) {
case 3:
- ast->dram_type = AST_DRAM_4Gx16;
- break;
- }
- } else {
- switch (data & 0x0c) {
- case 0:
- case 4:
- ast->dram_type = AST_DRAM_512Mx16;
+ div = 0x4;
break;
- case 8:
- if (data & 0x40)
- ast->dram_type = AST_DRAM_1Gx16;
- else
- ast->dram_type = AST_DRAM_512Mx32;
+ case 2:
+ case 1:
+ div = 0x2;
break;
- case 0xc:
- ast->dram_type = AST_DRAM_1Gx32;
+ default:
+ div = 0x1;
break;
}
+ ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000);
}
-
- data = ast_read32(ast, 0x10120);
- data2 = ast_read32(ast, 0x10170);
- if (data2 & 0x2000)
- ref_pll = 14318;
- else
- ref_pll = 12000;
-
- denum = data & 0x1f;
- num = (data & 0x3fe0) >> 5;
- data = (data & 0xc000) >> 14;
- switch (data) {
- case 3:
- div = 0x4;
- break;
- case 2:
- case 1:
- div = 0x2;
- break;
- default:
- div = 0x1;
- break;
- }
- ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000);
return 0;
}
diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c
index 30672a3df8a9..270e8fb2803f 100644
--- a/drivers/gpu/drm/ast/ast_post.c
+++ b/drivers/gpu/drm/ast/ast_post.c
@@ -375,12 +375,20 @@ void ast_post_gpu(struct drm_device *dev)
ast_enable_mmio(dev);
ast_set_def_ext_reg(dev);
- if (ast->chip == AST2300 || ast->chip == AST2400)
- ast_init_dram_2300(dev);
- else
- ast_init_dram_reg(dev);
+ if (ast->DisableP2A == false)
+ {
+ if (ast->chip == AST2300 || ast->chip == AST2400)
+ ast_init_dram_2300(dev);
+ else
+ ast_init_dram_reg(dev);
- ast_init_3rdtx(dev);
+ ast_init_3rdtx(dev);
+ }
+ else
+ {
+ if (ast->tx_chip_type != AST_TX_NONE)
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); /* Enable DVO */
+ }
}
/* AST 2300 DRAM settings */
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/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index ce0645d0c1e5..61e3a097a478 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -783,20 +783,23 @@ void psb_intel_lvds_init(struct drm_device *dev,
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
mode_dev->panel_fixed_mode =
drm_mode_duplicate(dev, scan);
+ DRM_DEBUG_KMS("Using mode from DDC\n");
goto out; /* FIXME: check for quirks */
}
}
/* Failed to get EDID, what about VBT? do we need this? */
- if (mode_dev->vbt_mode)
+ if (dev_priv->lfp_lvds_vbt_mode) {
mode_dev->panel_fixed_mode =
- drm_mode_duplicate(dev, mode_dev->vbt_mode);
+ drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
- if (!mode_dev->panel_fixed_mode)
- if (dev_priv->lfp_lvds_vbt_mode)
- mode_dev->panel_fixed_mode =
- drm_mode_duplicate(dev,
- dev_priv->lfp_lvds_vbt_mode);
+ if (mode_dev->panel_fixed_mode) {
+ mode_dev->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ DRM_DEBUG_KMS("Using mode from VBT\n");
+ goto out;
+ }
+ }
/*
* If we didn't get EDID, try checking if the panel is already turned
@@ -813,6 +816,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
if (mode_dev->panel_fixed_mode) {
mode_dev->panel_fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
+ DRM_DEBUG_KMS("Using pre-programmed mode\n");
goto out; /* FIXME: check for quirks */
}
}
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 f3a8a8416c7a..84125b3d1f95 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -13,6 +13,7 @@ msm_drm-y := \
hdmi/hdmi_connector.o \
hdmi/hdmi_hdcp.o \
hdmi/hdmi_i2c.o \
+ hdmi/hdmi_util.o \
hdmi/hdmi_phy_8960.o \
hdmi/hdmi_phy_8x60.o \
hdmi/hdmi_phy_8x74.o \
@@ -50,7 +51,8 @@ msm_drm-y := \
sde_dbg_evtlog.o \
sde_io_util.o \
dba_bridge.o \
- sde_edid_parser.o
+ sde_edid_parser.o \
+ sde_hdcp_1x.o
# use drm gpu driver only if qcom_kgsl driver not available
ifneq ($(CONFIG_QCOM_KGSL),y)
@@ -101,9 +103,11 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \
dsi-staging/dsi_display_test.o
msm_drm-$(CONFIG_DRM_SDE_HDMI) += \
+ hdmi-staging/sde_hdmi_util.o \
hdmi-staging/sde_hdmi.o \
hdmi-staging/sde_hdmi_bridge.o \
hdmi-staging/sde_hdmi_audio.o \
+ hdmi-staging/sde_hdmi_hdcp2p2.o \
msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
dsi/pll/dsi_pll_28nm.o
@@ -144,6 +148,8 @@ msm_drm-$(CONFIG_DRM_MSM) += \
msm_rd.o \
msm_ringbuffer.o \
msm_prop.o \
- msm_snapshot.o
+ msm_snapshot.o \
+ msm_submitqueue.o \
+ msm_trace_points.o
obj-$(CONFIG_DRM_MSM) += msm_drm.o
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 3caa460aa5ba..e27ea604ac5b 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -13,6 +13,7 @@
#include "msm_gem.h"
#include "msm_iommu.h"
+#include "msm_trace.h"
#include "a5xx_gpu.h"
#define SECURE_VA_START 0xc0000000
@@ -46,7 +47,6 @@ static void a5xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
static void a5xx_set_pagetable(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
struct msm_gem_address_space *aspace)
{
- struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct msm_mmu *mmu = aspace->mmu;
struct msm_iommu *iommu = to_msm_iommu(mmu);
@@ -75,17 +75,15 @@ static void a5xx_set_pagetable(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
* reload the pagetable if the current ring gets preempted out.
*/
OUT_PKT7(ring, CP_MEM_WRITE, 4);
- OUT_RING(ring, lower_32_bits(rbmemptr(adreno_gpu, ring->id, ttbr0)));
- OUT_RING(ring, upper_32_bits(rbmemptr(adreno_gpu, ring->id, ttbr0)));
+ OUT_RING(ring, lower_32_bits(rbmemptr(ring, ttbr0)));
+ OUT_RING(ring, upper_32_bits(rbmemptr(ring, ttbr0)));
OUT_RING(ring, lower_32_bits(iommu->ttbr0));
OUT_RING(ring, upper_32_bits(iommu->ttbr0));
/* Also write the current contextidr (ASID) */
OUT_PKT7(ring, CP_MEM_WRITE, 3);
- OUT_RING(ring, lower_32_bits(rbmemptr(adreno_gpu, ring->id,
- contextidr)));
- OUT_RING(ring, upper_32_bits(rbmemptr(adreno_gpu, ring->id,
- contextidr)));
+ OUT_RING(ring, lower_32_bits(rbmemptr(ring, contextidr)));
+ OUT_RING(ring, upper_32_bits(rbmemptr(ring, contextidr)));
OUT_RING(ring, iommu->contextidr);
/* Invalidate the draw state so we start off fresh */
@@ -103,12 +101,32 @@ static void a5xx_set_pagetable(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
OUT_RING(ring, 1);
}
+/* Inline PM4 code to get the current value of the 19.2 Mhz always on counter */
+static void a5xx_get_ticks(struct msm_ringbuffer *ring, uint64_t iova)
+{
+ /*
+ * Set bit[30] to make this command a 64 bit write operation.
+ * bits[18-29] is to specify number of consecutive registers
+ * to copy, so set this space with 2, since we want to copy
+ * data from REG_A5XX_RBBM_ALWAYSON_COUNTER_LO and [HI].
+ */
+
+ OUT_PKT7(ring, CP_REG_TO_MEM, 3);
+ OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO |
+ (1 << 30) | (2 << 18));
+ OUT_RING(ring, lower_32_bits(iova));
+ OUT_RING(ring, upper_32_bits(iova));
+}
+
static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
struct msm_ringbuffer *ring = gpu->rb[submit->ring];
unsigned int i, ibs = 0;
+ unsigned long flags;
+ u64 ticks;
+ ktime_t time;
a5xx_set_pagetable(gpu, ring, submit->aspace);
@@ -142,24 +160,15 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_RING(ring, 1);
}
- /* Record the always on counter before command execution */
- if (submit->profile_buf_iova) {
- uint64_t gpuaddr = submit->profile_buf_iova +
- offsetof(struct drm_msm_gem_submit_profile_buffer,
- ticks_submitted);
+ /* Record the GPU ticks at command start for kernel side profiling */
+ a5xx_get_ticks(ring,
+ RING_TICKS_IOVA(ring, submit->tick_index, started));
- /*
- * Set bit[30] to make this command a 64 bit write operation.
- * bits[18-29] is to specify number of consecutive registers
- * to copy, so set this space with 2, since we want to copy
- * data from REG_A5XX_RBBM_ALWAYSON_COUNTER_LO and [HI].
- */
- OUT_PKT7(ring, CP_REG_TO_MEM, 3);
- OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO |
- (1 << 30) | (2 << 18));
- OUT_RING(ring, lower_32_bits(gpuaddr));
- OUT_RING(ring, upper_32_bits(gpuaddr));
- }
+ /* And for the user profiling too if it is enabled */
+ if (submit->profile_buf_iova)
+ a5xx_get_ticks(ring, submit->profile_buf_iova +
+ offsetof(struct drm_msm_gem_submit_profile_buffer,
+ ticks_submitted));
/* Submit the commands */
for (i = 0; i < submit->nr_cmds; i++) {
@@ -193,18 +202,15 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_PKT7(ring, CP_YIELD_ENABLE, 1);
OUT_RING(ring, 0x01);
+ /* Record the GPU ticks at command retire for kernel side profiling */
+ a5xx_get_ticks(ring,
+ RING_TICKS_IOVA(ring, submit->tick_index, retired));
+
/* Record the always on counter after command execution */
- if (submit->profile_buf_iova) {
- uint64_t gpuaddr = submit->profile_buf_iova +
+ if (submit->profile_buf_iova)
+ a5xx_get_ticks(ring, submit->profile_buf_iova +
offsetof(struct drm_msm_gem_submit_profile_buffer,
- ticks_retired);
-
- OUT_PKT7(ring, CP_REG_TO_MEM, 3);
- OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO |
- (1 << 30) | (2 << 18));
- OUT_RING(ring, lower_32_bits(gpuaddr));
- OUT_RING(ring, upper_32_bits(gpuaddr));
- }
+ ticks_retired));
/* Write the fence to the scratch register */
OUT_PKT4(ring, REG_A5XX_CP_SCRATCH_REG(2), 1);
@@ -217,8 +223,8 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_PKT7(ring, CP_EVENT_WRITE, 4);
OUT_RING(ring, CACHE_FLUSH_TS | (1 << 31));
- OUT_RING(ring, lower_32_bits(rbmemptr(adreno_gpu, ring->id, fence)));
- OUT_RING(ring, upper_32_bits(rbmemptr(adreno_gpu, ring->id, fence)));
+ OUT_RING(ring, lower_32_bits(rbmemptr(ring, fence)));
+ OUT_RING(ring, upper_32_bits(rbmemptr(ring, fence)));
OUT_RING(ring, submit->fence);
if (submit->secure) {
@@ -240,33 +246,28 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
/* Set bit 0 to trigger an interrupt on preempt complete */
OUT_RING(ring, 0x01);
- if (submit->profile_buf_iova) {
- unsigned long flags;
- uint64_t ktime;
- struct drm_msm_gem_submit_profile_buffer *profile_buf =
- submit->profile_buf_vaddr;
-
- /*
- * With this profiling, we are trying to create closest
- * possible mapping between the CPU time domain(monotonic clock)
- * and the GPU time domain(ticks). In order to make this
- * happen, we need to briefly turn off interrupts to make sure
- * interrupts do not run between collecting these two samples.
- */
- local_irq_save(flags);
-
- profile_buf->ticks_queued = gpu_read64(gpu,
- REG_A5XX_RBBM_ALWAYSON_COUNTER_LO,
- REG_A5XX_RBBM_ALWAYSON_COUNTER_HI);
+ /*
+ * Get the current kernel time and ticks with interrupts off so we don't
+ * get interrupted between the operations and skew the numbers
+ */
- ktime = ktime_get_raw_ns();
+ local_irq_save(flags);
+ ticks = gpu_read64(gpu, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO,
+ REG_A5XX_RBBM_ALWAYSON_COUNTER_HI);
+ time = ktime_get_raw();
+ local_irq_restore(flags);
- local_irq_restore(flags);
+ if (submit->profile_buf) {
+ struct timespec64 ts = ktime_to_timespec64(time);
- profile_buf->queue_time = ktime;
- profile_buf->submit_time = ktime;
+ /* Write the data into the user-specified profile buffer */
+ submit->profile_buf->time.tv_sec = ts.tv_sec;
+ submit->profile_buf->time.tv_nsec = ts.tv_nsec;
+ submit->profile_buf->ticks_queued = ticks;
}
+ trace_msm_submitted(submit, ticks, ktime_to_ns(time));
+
a5xx_flush(gpu, ring);
/* Check to see if we need to start preemption */
@@ -477,32 +478,14 @@ static int a5xx_preempt_start(struct msm_gpu *gpu)
static struct drm_gem_object *a5xx_ucode_load_bo(struct msm_gpu *gpu,
const struct firmware *fw, u64 *iova)
{
- struct drm_device *drm = gpu->dev;
struct drm_gem_object *bo;
void *ptr;
- mutex_lock(&drm->struct_mutex);
- bo = msm_gem_new(drm, fw->size - 4,
- MSM_BO_UNCACHED | MSM_BO_GPU_READONLY);
- mutex_unlock(&drm->struct_mutex);
+ ptr = msm_gem_kernel_new(gpu->dev, fw->size - 4,
+ MSM_BO_UNCACHED | MSM_BO_GPU_READONLY, gpu->aspace, &bo, iova);
- if (IS_ERR(bo))
- return bo;
-
- ptr = msm_gem_vaddr(bo);
- if (!ptr) {
- drm_gem_object_unreference_unlocked(bo);
- return ERR_PTR(-ENOMEM);
- }
-
- if (iova) {
- int ret = msm_gem_get_iova(bo, gpu->aspace, iova);
-
- if (ret) {
- drm_gem_object_unreference_unlocked(bo);
- return ERR_PTR(ret);
- }
- }
+ if (IS_ERR(ptr))
+ return ERR_CAST(ptr);
memcpy(ptr, &fw->data[4], fw->size - 4);
return bo;
@@ -1408,8 +1391,8 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev)
* Set the user domain range to fall into the TTBR1 region for global
* objects
*/
- a5xx_config.va_start = 0x800000000;
- a5xx_config.va_end = 0x8ffffffff;
+ a5xx_config.va_start = 0xfffffff000000000ULL;
+ a5xx_config.va_end = 0xffffffffffffffffULL;
a5xx_config.secure_va_start = SECURE_VA_START;
a5xx_config.secure_va_end = SECURE_VA_START + SECURE_VA_SIZE - 1;
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_power.c b/drivers/gpu/drm/msm/adreno/a5xx_power.c
index e04feaadefb9..647b61313fc2 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_power.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_power.c
@@ -458,20 +458,10 @@ void a5xx_gpmu_ucode_init(struct msm_gpu *gpu)
*/
bosize = (cmds_size + (cmds_size / TYPE4_MAX_PAYLOAD) + 1) << 2;
- mutex_lock(&drm->struct_mutex);
- a5xx_gpu->gpmu_bo = msm_gem_new(drm, bosize,
- MSM_BO_UNCACHED | MSM_BO_GPU_READONLY);
- mutex_unlock(&drm->struct_mutex);
-
- if (IS_ERR(a5xx_gpu->gpmu_bo))
- goto err;
-
- if (msm_gem_get_iova(a5xx_gpu->gpmu_bo, gpu->aspace,
- &a5xx_gpu->gpmu_iova))
- goto err;
-
- ptr = msm_gem_vaddr(a5xx_gpu->gpmu_bo);
- if (!ptr)
+ ptr = msm_gem_kernel_new(drm, bosize,
+ MSM_BO_UNCACHED | MSM_BO_GPU_READONLY, gpu->aspace,
+ &a5xx_gpu->gpmu_bo, &a5xx_gpu->gpmu_iova);
+ if (IS_ERR(ptr))
goto err;
while (cmds_size > 0) {
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
index 648494c75abc..57ef366cf82c 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
@@ -15,43 +15,6 @@
#include "msm_iommu.h"
#include "a5xx_gpu.h"
-static void *alloc_kernel_bo(struct drm_device *drm, struct msm_gpu *gpu,
- size_t size, uint32_t flags, struct drm_gem_object **bo,
- u64 *iova)
-{
- struct drm_gem_object *_bo;
- u64 _iova;
- void *ptr;
- int ret;
-
- mutex_lock(&drm->struct_mutex);
- _bo = msm_gem_new(drm, size, flags);
- mutex_unlock(&drm->struct_mutex);
-
- if (IS_ERR(_bo))
- return _bo;
-
- ret = msm_gem_get_iova(_bo, gpu->aspace, &_iova);
- if (ret)
- goto out;
-
- ptr = msm_gem_vaddr(_bo);
- if (!ptr) {
- ret = -ENOMEM;
- goto out;
- }
-
- if (bo)
- *bo = _bo;
- if (iova)
- *iova = _iova;
-
- return ptr;
-out:
- drm_gem_object_unreference_unlocked(_bo);
- return ERR_PTR(ret);
-}
-
/*
* Try to transition the preemption state from old to new. Return
* true on success or false if the original state wasn't 'old'
@@ -102,7 +65,6 @@ static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
/* Return the highest priority ringbuffer with something in it */
static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu)
{
- struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
unsigned long flags;
int i;
@@ -111,7 +73,7 @@ static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu)
struct msm_ringbuffer *ring = gpu->rb[i];
spin_lock_irqsave(&ring->lock, flags);
- empty = (get_wptr(ring) == adreno_gpu->memptrs->rptr[ring->id]);
+ empty = (get_wptr(ring) == ring->memptrs->rptr);
spin_unlock_irqrestore(&ring->lock, flags);
if (!empty)
@@ -178,10 +140,8 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
/* Set the SMMU info for the preemption */
if (a5xx_gpu->smmu_info) {
- a5xx_gpu->smmu_info->ttbr0 =
- adreno_gpu->memptrs->ttbr0[ring->id];
- a5xx_gpu->smmu_info->contextidr =
- adreno_gpu->memptrs->contextidr[ring->id];
+ a5xx_gpu->smmu_info->ttbr0 = ring->memptrs->ttbr0;
+ a5xx_gpu->smmu_info->contextidr = ring->memptrs->contextidr;
}
/* Set the address of the incoming preemption record */
@@ -280,10 +240,10 @@ static int preempt_init_ring(struct a5xx_gpu *a5xx_gpu,
struct drm_gem_object *bo;
u64 iova;
- ptr = alloc_kernel_bo(gpu->dev, gpu,
+ ptr = msm_gem_kernel_new(gpu->dev,
A5XX_PREEMPT_RECORD_SIZE + A5XX_PREEMPT_COUNTER_SIZE,
MSM_BO_UNCACHED | MSM_BO_PRIVILEGED,
- &bo, &iova);
+ gpu->aspace, &bo, &iova);
if (IS_ERR(ptr))
return PTR_ERR(ptr);
@@ -298,7 +258,7 @@ static int preempt_init_ring(struct a5xx_gpu *a5xx_gpu,
ptr->info = 0;
ptr->data = 0;
ptr->cntl = MSM_GPU_RB_CNTL_DEFAULT;
- ptr->rptr_addr = rbmemptr(adreno_gpu, ring->id, rptr);
+ ptr->rptr_addr = rbmemptr(ring, rptr);
ptr->counter = iova + A5XX_PREEMPT_RECORD_SIZE;
return 0;
@@ -354,10 +314,10 @@ void a5xx_preempt_init(struct msm_gpu *gpu)
}
if (msm_iommu_allow_dynamic(gpu->aspace->mmu)) {
- ptr = alloc_kernel_bo(gpu->dev, gpu,
+ ptr = msm_gem_kernel_new(gpu->dev,
sizeof(struct a5xx_smmu_info),
MSM_BO_UNCACHED | MSM_BO_PRIVILEGED,
- &bo, &iova);
+ gpu->aspace, &bo, &iova);
if (IS_ERR(ptr))
goto fail;
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c b/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c
index 690e6f546e60..d1c1ab460c95 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c
@@ -214,27 +214,14 @@ struct crashdump {
static int crashdump_init(struct msm_gpu *gpu, struct crashdump *crashdump)
{
- struct drm_device *drm = gpu->dev;
- int ret = -ENOMEM;
-
- crashdump->bo = msm_gem_new(drm, CRASHDUMP_BO_SIZE, MSM_BO_UNCACHED);
- if (IS_ERR(crashdump->bo)) {
- ret = PTR_ERR(crashdump->bo);
- crashdump->bo = NULL;
- return ret;
- }
-
- crashdump->ptr = msm_gem_vaddr_locked(crashdump->bo);
- if (!crashdump->ptr)
- goto out;
-
- ret = msm_gem_get_iova_locked(crashdump->bo, gpu->aspace,
- &crashdump->iova);
-
-out:
- if (ret) {
- drm_gem_object_unreference(crashdump->bo);
- crashdump->bo = NULL;
+ int ret = 0;
+
+ crashdump->ptr = msm_gem_kernel_new_locked(gpu->dev,
+ CRASHDUMP_BO_SIZE, MSM_BO_UNCACHED,
+ gpu->aspace, &crashdump->bo, &crashdump->iova);
+ if (IS_ERR(crashdump->ptr)) {
+ ret = PTR_ERR(crashdump->ptr);
+ crashdump->ptr = NULL;
}
return ret;
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index d3cb497411c4..2a5683e3034b 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -51,6 +51,9 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
if (adreno_gpu->funcs->get_timestamp)
return adreno_gpu->funcs->get_timestamp(gpu, value);
return -EINVAL;
+ case MSM_PARAM_NR_RINGS:
+ *value = gpu->nr_rings;
+ return 0;
default:
DBG("%s: invalid param: %u", gpu->name, param);
return -EINVAL;
@@ -90,7 +93,7 @@ int adreno_hw_init(struct msm_gpu *gpu)
REG_ADRENO_CP_RB_BASE_HI, gpu->rb[0]->iova);
adreno_gpu_write64(adreno_gpu, REG_ADRENO_CP_RB_RPTR_ADDR,
- REG_ADRENO_CP_RB_RPTR_ADDR_HI, rbmemptr(adreno_gpu, 0, rptr));
+ REG_ADRENO_CP_RB_RPTR_ADDR_HI, rbmemptr(gpu->rb[0], rptr));
return 0;
}
@@ -106,10 +109,11 @@ static uint32_t get_rptr(struct adreno_gpu *adreno_gpu,
* ensure that it won't be. If not then this is why your
* a430 stopped working.
*/
- return adreno_gpu->memptrs->rptr[ring->id] = adreno_gpu_read(
- adreno_gpu, REG_ADRENO_CP_RB_RPTR);
- } else
- return adreno_gpu->memptrs->rptr[ring->id];
+ return ring->memptrs->rptr =
+ adreno_gpu_read(adreno_gpu, REG_ADRENO_CP_RB_RPTR);
+ }
+
+ return ring->memptrs->rptr;
}
struct msm_ringbuffer *adreno_active_ring(struct msm_gpu *gpu)
@@ -128,17 +132,11 @@ uint32_t adreno_submitted_fence(struct msm_gpu *gpu,
uint32_t adreno_last_fence(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
{
- struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
-
- if (!ring)
- return 0;
-
- return adreno_gpu->memptrs->fence[ring->id];
+ return ring ? ring->memptrs->fence : 0;
}
void adreno_recover(struct msm_gpu *gpu)
{
- struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct drm_device *dev = gpu->dev;
struct msm_ringbuffer *ring;
int ret, i;
@@ -156,9 +154,8 @@ void adreno_recover(struct msm_gpu *gpu)
ring->next = ring->start;
/* reset completed fence seqno, discard anything pending: */
- adreno_gpu->memptrs->fence[ring->id] =
- adreno_submitted_fence(gpu, ring);
- adreno_gpu->memptrs->rptr[ring->id] = 0;
+ ring->memptrs->fence = adreno_submitted_fence(gpu, ring);
+ ring->memptrs->rptr = 0;
}
gpu->funcs->pm_resume(gpu);
@@ -213,7 +210,7 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_PKT3(ring, CP_EVENT_WRITE, 3);
OUT_RING(ring, CACHE_FLUSH_TS);
- OUT_RING(ring, rbmemptr(adreno_gpu, ring->id, fence));
+ OUT_RING(ring, rbmemptr(ring, fence));
OUT_RING(ring, submit->fence);
/* we could maybe be clever and only CP_COND_EXEC the interrupt: */
@@ -516,7 +513,6 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
{
struct adreno_platform_config *config = pdev->dev.platform_data;
struct msm_gpu *gpu = &adreno_gpu->base;
- struct msm_mmu *mmu;
int ret;
adreno_gpu->funcs = funcs;
@@ -541,79 +537,19 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
}
ret = request_firmware(&adreno_gpu->pfp, adreno_gpu->info->pfpfw, drm->dev);
- if (ret) {
+ if (ret)
dev_err(drm->dev, "failed to load %s PFP firmware: %d\n",
adreno_gpu->info->pfpfw, ret);
- return ret;
- }
- mmu = gpu->aspace->mmu;
- if (mmu) {
- ret = mmu->funcs->attach(mmu, NULL, 0);
- if (ret)
- return ret;
- }
-
- if (gpu->secure_aspace) {
- mmu = gpu->secure_aspace->mmu;
- if (mmu) {
- ret = mmu->funcs->attach(mmu, NULL, 0);
- if (ret)
- return ret;
- }
- }
-
- mutex_lock(&drm->struct_mutex);
- adreno_gpu->memptrs_bo = msm_gem_new(drm, sizeof(*adreno_gpu->memptrs),
- MSM_BO_UNCACHED);
- mutex_unlock(&drm->struct_mutex);
- if (IS_ERR(adreno_gpu->memptrs_bo)) {
- ret = PTR_ERR(adreno_gpu->memptrs_bo);
- adreno_gpu->memptrs_bo = NULL;
- dev_err(drm->dev, "could not allocate memptrs: %d\n", ret);
- return ret;
- }
-
- adreno_gpu->memptrs = msm_gem_vaddr(adreno_gpu->memptrs_bo);
- if (!adreno_gpu->memptrs) {
- dev_err(drm->dev, "could not vmap memptrs\n");
- return -ENOMEM;
- }
-
- ret = msm_gem_get_iova(adreno_gpu->memptrs_bo, gpu->aspace,
- &adreno_gpu->memptrs_iova);
- if (ret) {
- dev_err(drm->dev, "could not map memptrs: %d\n", ret);
- return ret;
- }
-
- return 0;
+ return ret;
}
void adreno_gpu_cleanup(struct adreno_gpu *gpu)
{
- struct msm_gem_address_space *aspace = gpu->base.aspace;
-
- if (gpu->memptrs_bo) {
- if (gpu->memptrs_iova)
- msm_gem_put_iova(gpu->memptrs_bo, aspace);
- drm_gem_object_unreference_unlocked(gpu->memptrs_bo);
- }
release_firmware(gpu->pm4);
release_firmware(gpu->pfp);
msm_gpu_cleanup(&gpu->base);
-
- if (aspace) {
- aspace->mmu->funcs->detach(aspace->mmu);
- msm_gem_address_space_put(aspace);
- }
-
- if (gpu->base.secure_aspace) {
- aspace = gpu->base.secure_aspace;
- aspace->mmu->funcs->detach(aspace->mmu);
- msm_gem_address_space_put(aspace);
- }
}
static void adreno_snapshot_os(struct msm_gpu *gpu,
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index 9e622fa06ce4..c894956fb5e8 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -83,22 +83,6 @@ struct adreno_info {
const struct adreno_info *adreno_info(struct adreno_rev rev);
-#define _sizeof(member) \
- sizeof(((struct adreno_rbmemptrs *) 0)->member[0])
-
-#define _base(adreno_gpu, member) \
- ((adreno_gpu)->memptrs_iova + offsetof(struct adreno_rbmemptrs, member))
-
-#define rbmemptr(adreno_gpu, index, member) \
- (_base((adreno_gpu), member) + ((index) * _sizeof(member)))
-
-struct adreno_rbmemptrs {
- volatile uint32_t rptr[MSM_GPU_MAX_RINGS];
- volatile uint32_t fence[MSM_GPU_MAX_RINGS];
- volatile uint64_t ttbr0[MSM_GPU_MAX_RINGS];
- volatile unsigned int contextidr[MSM_GPU_MAX_RINGS];
-};
-
struct adreno_counter {
u32 lo;
u32 hi;
@@ -137,13 +121,6 @@ struct adreno_gpu {
/* firmware: */
const struct firmware *pm4, *pfp;
- /* ringbuffer rptr/wptr: */
- // TODO should this be in msm_ringbuffer? I think it would be
- // different for z180..
- struct adreno_rbmemptrs *memptrs;
- struct drm_gem_object *memptrs_bo;
- uint64_t memptrs_iova;
-
/*
* Register offsets are different between some GPUs.
* GPU specific offsets will be exported by GPU specific
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index c34713a13332..309401eb3093 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -20,6 +20,7 @@
#include "msm_kms.h"
#include "sde_connector.h"
#include "dsi_drm.h"
+#include "sde_trace.h"
#define to_dsi_bridge(x) container_of((x), struct dsi_bridge, base)
#define to_dsi_state(x) container_of((x), struct dsi_connector_state, base)
@@ -138,19 +139,24 @@ static void dsi_bridge_pre_enable(struct drm_bridge *bridge)
return;
}
+ SDE_ATRACE_BEGIN("dsi_bridge_pre_enable");
rc = dsi_display_prepare(c_bridge->display);
if (rc) {
pr_err("[%d] DSI display prepare failed, rc=%d\n",
c_bridge->id, rc);
+ SDE_ATRACE_END("dsi_bridge_pre_enable");
return;
}
+ SDE_ATRACE_BEGIN("dsi_display_enable");
rc = dsi_display_enable(c_bridge->display);
if (rc) {
pr_err("[%d] DSI display enable failed, rc=%d\n",
c_bridge->id, rc);
(void)dsi_display_unprepare(c_bridge->display);
}
+ SDE_ATRACE_END("dsi_display_enable");
+ SDE_ATRACE_END("dsi_bridge_pre_enable");
}
static void dsi_bridge_enable(struct drm_bridge *bridge)
@@ -201,19 +207,25 @@ static void dsi_bridge_post_disable(struct drm_bridge *bridge)
return;
}
+ SDE_ATRACE_BEGIN("dsi_bridge_post_disable");
+ SDE_ATRACE_BEGIN("dsi_display_disable");
rc = dsi_display_disable(c_bridge->display);
if (rc) {
pr_err("[%d] DSI display disable failed, rc=%d\n",
c_bridge->id, rc);
+ SDE_ATRACE_END("dsi_display_disable");
return;
}
+ SDE_ATRACE_END("dsi_display_disable");
rc = dsi_display_unprepare(c_bridge->display);
if (rc) {
pr_err("[%d] DSI display unprepare failed, rc=%d\n",
c_bridge->id, rc);
+ SDE_ATRACE_END("dsi_bridge_post_disable");
return;
}
+ SDE_ATRACE_END("dsi_bridge_post_disable");
}
static void dsi_bridge_mode_set(struct drm_bridge *bridge,
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index 4580a6e3c877..e2b8deda46c2 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -838,22 +838,19 @@ static int dsi_tx_buf_alloc(struct msm_dsi_host *msm_host, int size)
int ret;
u64 iova;
- mutex_lock(&dev->struct_mutex);
msm_host->tx_gem_obj = msm_gem_new(dev, size, MSM_BO_UNCACHED);
if (IS_ERR(msm_host->tx_gem_obj)) {
ret = PTR_ERR(msm_host->tx_gem_obj);
pr_err("%s: failed to allocate gem, %d\n", __func__, ret);
msm_host->tx_gem_obj = NULL;
- mutex_unlock(&dev->struct_mutex);
return ret;
}
- ret = msm_gem_get_iova_locked(msm_host->tx_gem_obj, 0, &iova);
+ ret = msm_gem_get_iova(msm_host->tx_gem_obj, NULL, &iova);
if (ret) {
pr_err("%s: failed to get iova, %d\n", __func__, ret);
return ret;
}
- mutex_unlock(&dev->struct_mutex);
if (iova & 0x07) {
pr_err("%s: buf NOT 8 bytes aligned\n", __func__);
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 4c70472bd338..2ee75a2c1039 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -29,6 +29,8 @@
#include "sde_connector.h"
#include "msm_drv.h"
#include "sde_hdmi.h"
+#include "sde_hdmi_regs.h"
+#include "hdmi.h"
static DEFINE_MUTEX(sde_hdmi_list_lock);
static LIST_HEAD(sde_hdmi_list);
@@ -49,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"},
{}
@@ -367,6 +372,135 @@ static ssize_t _sde_hdmi_edid_vendor_name_read(struct file *file,
return len;
}
+static ssize_t _sde_hdmi_src_hdcp14_support_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[SZ_128];
+ u32 len = 0;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl) {
+ SDE_ERROR("hdmi is NULL\n");
+ return -ENOMEM;
+ }
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ if (display->hdcp14_present)
+ len += snprintf(buf, SZ_128 - len, "true\n");
+ else
+ len += snprintf(buf, SZ_128 - len, "false\n");
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ SDE_HDMI_DEBUG("%s - ", __func__);
+ return len;
+}
+
+static ssize_t _sde_hdmi_src_hdcp22_support_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[SZ_128];
+ u32 len = 0;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl) {
+ SDE_ERROR("hdmi is NULL\n");
+ return -ENOMEM;
+ }
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ if (display->src_hdcp22_support)
+ len += snprintf(buf, SZ_128 - len, "true\n");
+ else
+ len += snprintf(buf, SZ_128 - len, "false\n");
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ SDE_HDMI_DEBUG("%s - ", __func__);
+ return len;
+}
+
+static ssize_t _sde_hdmi_sink_hdcp22_support_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[SZ_128];
+ u32 len = 0;
+
+ if (!display)
+ return -ENODEV;
+
+ if (!display->ctrl.ctrl) {
+ SDE_ERROR("hdmi is NULL\n");
+ return -ENOMEM;
+ }
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ if (display->sink_hdcp22_support)
+ len += snprintf(buf, SZ_128 - len, "true\n");
+ else
+ len += snprintf(buf, SZ_128 - len, "false\n");
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ SDE_HDMI_DEBUG("%s - ", __func__);
+ return len;
+}
+
+static ssize_t _sde_hdmi_hdcp_state_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[SZ_128];
+ u32 len = 0;
+
+ if (!display)
+ return -ENODEV;
+
+ SDE_HDMI_DEBUG("%s +", __func__);
+ if (*ppos)
+ return 0;
+
+ len += snprintf(buf, SZ_128 - len, "HDCP state : %s\n",
+ sde_hdcp_state_name(display->hdcp_status));
+
+ if (copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ SDE_HDMI_DEBUG("%s - ", __func__);
+ return len;
+}
+
static const struct file_operations dump_info_fops = {
.open = simple_open,
.read = _sde_hdmi_debugfs_dump_info_read,
@@ -402,6 +536,26 @@ static const struct file_operations edid_vendor_name_fops = {
.read = _sde_hdmi_edid_vendor_name_read,
};
+static const struct file_operations hdcp_src_14_support_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_src_hdcp14_support_read,
+};
+
+static const struct file_operations hdcp_src_22_support_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_src_hdcp22_support_read,
+};
+
+static const struct file_operations hdcp_sink_22_support_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_sink_hdcp22_support_read,
+};
+
+static const struct file_operations sde_hdmi_hdcp_state_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_hdcp_state_read,
+};
+
static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in)
{
u32 pclk_delta, pclk;
@@ -422,6 +576,118 @@ static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in)
return pclk_clip;
}
+static void sde_hdmi_tx_hdcp_cb(void *ptr, enum sde_hdcp_states status)
+{
+ struct sde_hdmi *hdmi_ctrl = (struct sde_hdmi *)ptr;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+ hdmi_ctrl->hdcp_status = status;
+ queue_delayed_work(hdmi->workq, &hdmi_ctrl->hdcp_cb_work, HZ/4);
+}
+
+void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl)
+{
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (hdmi_ctrl->hdcp_ops)
+ hdmi_ctrl->hdcp_ops->off(hdmi_ctrl->hdcp_data);
+
+ flush_delayed_work(&hdmi_ctrl->hdcp_cb_work);
+
+ hdmi_ctrl->hdcp_ops = NULL;
+}
+
+static void sde_hdmi_tx_hdcp_cb_work(struct work_struct *work)
+{
+ struct sde_hdmi *hdmi_ctrl = NULL;
+ struct delayed_work *dw = to_delayed_work(work);
+ int rc = 0;
+ struct hdmi *hdmi;
+
+ hdmi_ctrl = container_of(dw, struct sde_hdmi, hdcp_cb_work);
+ if (!hdmi_ctrl) {
+ DEV_DBG("%s: invalid input\n", __func__);
+ return;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ switch (hdmi_ctrl->hdcp_status) {
+ case HDCP_STATE_AUTHENTICATED:
+ hdmi_ctrl->auth_state = true;
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl) &&
+ sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) {
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ }
+
+ if (hdmi_ctrl->hdcp1_use_sw_keys &&
+ hdmi_ctrl->hdcp14_present) {
+ if (!hdmi_ctrl->hdcp22_present)
+ hdcp1_set_enc(true);
+ }
+ break;
+ case HDCP_STATE_AUTH_FAIL:
+ if (hdmi_ctrl->hdcp1_use_sw_keys && hdmi_ctrl->hdcp14_present) {
+ if (hdmi_ctrl->auth_state && !hdmi_ctrl->hdcp22_present)
+ hdcp1_set_enc(false);
+ }
+
+ hdmi_ctrl->auth_state = false;
+
+ if (sde_hdmi_tx_is_encryption_set(hdmi_ctrl) ||
+ !sde_hdmi_tx_is_stream_shareable(hdmi_ctrl))
+ rc = sde_hdmi_config_avmute(hdmi, true);
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl)) {
+ pr_debug("%s: Reauthenticating\n", __func__);
+ if (hdmi_ctrl->hdcp_ops && hdmi_ctrl->hdcp_data) {
+ rc = hdmi_ctrl->hdcp_ops->reauthenticate(
+ hdmi_ctrl->hdcp_data);
+ if (rc)
+ pr_err("%s: HDCP reauth failed. rc=%d\n",
+ __func__, rc);
+ } else
+ pr_err("%s: NULL HDCP Ops and Data\n",
+ __func__);
+ } else {
+ pr_debug("%s: Not reauthenticating. Cable not conn\n",
+ __func__);
+ }
+
+ break;
+ case HDCP_STATE_AUTH_ENC_NONE:
+ hdmi_ctrl->enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl))
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ break;
+ case HDCP_STATE_AUTH_ENC_1X:
+ case HDCP_STATE_AUTH_ENC_2P2:
+ hdmi_ctrl->enc_lvl = hdmi_ctrl->hdcp_status;
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl) &&
+ sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) {
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ } else {
+ rc = sde_hdmi_config_avmute(hdmi, true);
+ }
+ break;
+ default:
+ break;
+ /* do nothing */
+ }
+}
+
/**
* _sde_hdmi_update_pll_delta() - Update the HDMI pixel clock as per input ppm
*
@@ -439,13 +705,22 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
u64 clip_pclk;
int rc = 0;
+ mutex_lock(&display->display_lock);
+
if (!hdmi->power_on || !display->connected) {
SDE_ERROR("HDMI display is not ready\n");
+ mutex_unlock(&display->display_lock);
+ return -EINVAL;
+ }
+
+ if (!display->pll_update_enable) {
+ SDE_ERROR("PLL update function is not enabled\n");
+ mutex_unlock(&display->display_lock);
return -EINVAL;
}
/* get current pclk */
- cur_pclk = hdmi->pixclock;
+ cur_pclk = hdmi->actual_pixclock;
/* get desired pclk */
dst_pclk = cur_pclk * (1000000000 + ppm);
do_div(dst_pclk, 1000000000);
@@ -459,13 +734,16 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
rc = clk_set_rate(hdmi->pwr_clks[0], clip_pclk);
if (rc < 0) {
- SDE_ERROR("PLL update failed, reset clock rate\n");
+ SDE_ERROR("HDMI PLL update failed\n");
+ mutex_unlock(&display->display_lock);
return rc;
}
- hdmi->pixclock = clip_pclk;
+ hdmi->actual_pixclock = clip_pclk;
}
+ mutex_unlock(&display->display_lock);
+
return rc;
}
@@ -501,12 +779,119 @@ static const struct file_operations pll_delta_fops = {
.write = _sde_hdmi_debugfs_pll_delta_write,
};
+/**
+ * _sde_hdmi_enable_pll_update() - Enable the HDMI PLL update function
+ *
+ * @enable: non-zero to enable PLL update function, 0 to disable.
+ * return: 0 on success, non-zero in case of failure.
+ *
+ */
+static int _sde_hdmi_enable_pll_update(struct sde_hdmi *display, s32 enable)
+{
+ struct hdmi *hdmi = display->ctrl.ctrl;
+ int rc = 0;
+
+ mutex_lock(&display->display_lock);
+
+ if (!hdmi->power_on || !display->connected) {
+ SDE_ERROR("HDMI display is not ready\n");
+ mutex_unlock(&display->display_lock);
+ return -EINVAL;
+ }
+
+ if (!enable && hdmi->actual_pixclock != hdmi->pixclock) {
+ /* reset pixel clock when disable */
+ rc = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock);
+ if (rc < 0) {
+ SDE_ERROR("reset clock rate failed\n");
+ mutex_unlock(&display->display_lock);
+ return rc;
+ }
+ }
+ hdmi->actual_pixclock = hdmi->pixclock;
+
+ display->pll_update_enable = !!enable;
+
+ mutex_unlock(&display->display_lock);
+
+ SDE_DEBUG("HDMI PLL update: %s\n",
+ display->pll_update_enable ? "enable" : "disable");
+
+ return rc;
+}
+
+static ssize_t _sde_hdmi_debugfs_pll_enable_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char *buf;
+ u32 len = 0;
+
+ if (!display)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ buf = kzalloc(SZ_1K, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len += snprintf(buf, SZ_4K, "%s\n",
+ display->pll_update_enable ? "enable" : "disable");
+
+ if (copy_to_user(buff, buf, len)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ *ppos += len;
+
+ kfree(buf);
+ return len;
+}
+
+static ssize_t _sde_hdmi_debugfs_pll_enable_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[10];
+ int enable = 0;
+
+ if (!display)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0; /* end of string */
+
+ if (kstrtoint(buf, 0, &enable))
+ return -EFAULT;
+
+ _sde_hdmi_enable_pll_update(display, enable);
+
+ return count;
+}
+
+static const struct file_operations pll_enable_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_debugfs_pll_enable_read,
+ .write = _sde_hdmi_debugfs_pll_enable_write,
+};
+
static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
{
int rc = 0;
struct dentry *dir, *dump_file, *edid_modes;
struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info;
- struct dentry *edid_vcdb_info, *edid_vendor_name, *pll_file;
+ struct dentry *edid_vcdb_info, *edid_vendor_name;
+ struct dentry *src_hdcp14_support, *src_hdcp22_support;
+ struct dentry *sink_hdcp22_support, *hdmi_hdcp_state;
+ struct dentry *pll_delta_file, *pll_enable_file;
dir = debugfs_create_dir(display->name, NULL);
if (!dir) {
@@ -528,18 +913,30 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
goto error_remove_dir;
}
- pll_file = debugfs_create_file("pll_delta",
+ pll_delta_file = debugfs_create_file("pll_delta",
0644,
dir,
display,
&pll_delta_fops);
- if (IS_ERR_OR_NULL(pll_file)) {
- rc = PTR_ERR(pll_file);
+ if (IS_ERR_OR_NULL(pll_delta_file)) {
+ rc = PTR_ERR(pll_delta_file);
SDE_ERROR("[%s]debugfs create pll_delta file failed, rc=%d\n",
display->name, rc);
goto error_remove_dir;
}
+ pll_enable_file = debugfs_create_file("pll_enable",
+ 0644,
+ dir,
+ display,
+ &pll_enable_fops);
+ if (IS_ERR_OR_NULL(pll_enable_file)) {
+ rc = PTR_ERR(pll_enable_file);
+ SDE_ERROR("[%s]debugfs create pll_enable file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
edid_modes = debugfs_create_file("edid_modes",
0444,
dir,
@@ -617,6 +1014,58 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
goto error_remove_dir;
}
+ src_hdcp14_support = debugfs_create_file("src_hdcp14_support",
+ 0444,
+ dir,
+ display,
+ &hdcp_src_14_support_fops);
+
+ if (IS_ERR_OR_NULL(src_hdcp14_support)) {
+ rc = PTR_ERR(src_hdcp14_support);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ src_hdcp22_support = debugfs_create_file("src_hdcp22_support",
+ 0444,
+ dir,
+ display,
+ &hdcp_src_22_support_fops);
+
+ if (IS_ERR_OR_NULL(src_hdcp22_support)) {
+ rc = PTR_ERR(src_hdcp22_support);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ sink_hdcp22_support = debugfs_create_file("sink_hdcp22_support",
+ 0444,
+ dir,
+ display,
+ &hdcp_sink_22_support_fops);
+
+ if (IS_ERR_OR_NULL(sink_hdcp22_support)) {
+ rc = PTR_ERR(sink_hdcp22_support);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ hdmi_hdcp_state = debugfs_create_file("hdmi_hdcp_state",
+ 0444,
+ dir,
+ display,
+ &sde_hdmi_hdcp_state_fops);
+
+ if (IS_ERR_OR_NULL(hdmi_hdcp_state)) {
+ rc = PTR_ERR(hdmi_hdcp_state);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
display->root = dir;
return rc;
error_remove_dir:
@@ -907,6 +1356,34 @@ static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi)
}
}
+static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display)
+{
+ struct edid *edid = display->edid_ctrl->edid;
+
+ if (edid)
+ cec_notifier_set_phys_addr_from_edid(display->notifier, edid);
+ else
+ cec_notifier_set_phys_addr(display->notifier,
+ CEC_PHYS_ADDR_INVALID);
+
+}
+
+static void _sde_hdmi_init_ddc(struct sde_hdmi *display, struct hdmi *hdmi)
+{
+ display->ddc_ctrl.io = &display->io[HDMI_TX_CORE_IO];
+ init_completion(&display->ddc_ctrl.rx_status_done);
+}
+
+static void _sde_hdmi_map_regs(struct sde_hdmi *display, struct hdmi *hdmi)
+{
+ display->io[HDMI_TX_CORE_IO].base = hdmi->mmio;
+ display->io[HDMI_TX_CORE_IO].len = hdmi->mmio_len;
+ display->io[HDMI_TX_QFPROM_IO].base = hdmi->qfprom_mmio;
+ display->io[HDMI_TX_QFPROM_IO].len = hdmi->qfprom_mmio_len;
+ display->io[HDMI_TX_HDCP_IO].base = hdmi->hdcp_mmio;
+ display->io[HDMI_TX_HDCP_IO].len = hdmi->hdcp_mmio_len;
+}
+
static void _sde_hdmi_hotplug_work(struct work_struct *work)
{
struct sde_hdmi *sde_hdmi =
@@ -936,6 +1413,7 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work)
sde_free_edid((void **)&sde_hdmi->edid_ctrl);
drm_helper_hpd_irq_event(connector->dev);
+ _sde_hdmi_cec_update_phys_addr(sde_hdmi);
}
static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi)
@@ -982,26 +1460,39 @@ static void _sde_hdmi_cec_irq(struct sde_hdmi *sde_hdmi)
static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id)
{
- struct sde_hdmi *sde_hdmi = dev_id;
+ struct sde_hdmi *display = dev_id;
struct hdmi *hdmi;
- if (!sde_hdmi || !sde_hdmi->ctrl.ctrl) {
- SDE_ERROR("sde_hdmi=%p or hdmi is NULL\n", sde_hdmi);
+ if (!display || !display->ctrl.ctrl) {
+ SDE_ERROR("sde_hdmi=%pK or hdmi is NULL\n", display);
return IRQ_NONE;
}
- hdmi = sde_hdmi->ctrl.ctrl;
+
+ hdmi = display->ctrl.ctrl;
/* Process HPD: */
- _sde_hdmi_connector_irq(sde_hdmi);
+ _sde_hdmi_connector_irq(display);
+
+ /* Process Scrambling ISR */
+ sde_hdmi_ddc_scrambling_isr((void *)display);
+
+ /* Process DDC2 */
+ sde_hdmi_ddc_hdcp2p2_isr((void *)display);
/* Process DDC: */
hdmi_i2c_irq(hdmi->i2c);
/* Process HDCP: */
- if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported)
- hdmi_hdcp_ctrl_irq(hdmi->hdcp_ctrl);
+ if (display->hdcp_ops && display->hdcp_data) {
+ if (display->hdcp_ops->isr) {
+ if (display->hdcp_ops->isr(
+ display->hdcp_data))
+ DEV_ERR("%s: hdcp_1x_isr failed\n",
+ __func__);
+ }
+ }
/* Process CEC: */
- _sde_hdmi_cec_irq(sde_hdmi);
+ _sde_hdmi_cec_irq(display);
return IRQ_HANDLED;
}
@@ -1177,84 +1668,8 @@ void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
power_on ? "Enable" : "Disable", ctrl);
}
-int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len)
-{
- int rc;
- int retry = 5;
- struct i2c_msg msgs[] = {
- {
- .addr = addr >> 1,
- .flags = 0,
- .len = 1,
- .buf = &offset,
- }, {
- .addr = addr >> 1,
- .flags = I2C_M_RD,
- .len = data_len,
- .buf = data,
- }
- };
-
- SDE_HDMI_DEBUG("Start DDC read");
- retry:
- rc = i2c_transfer(hdmi->i2c, msgs, 2);
-
- retry--;
- if (rc == 2)
- rc = 0;
- else if (retry > 0)
- goto retry;
- else
- rc = -EIO;
-
- SDE_HDMI_DEBUG("End DDC read %d", rc);
-
- return rc;
-}
-
#define DDC_WRITE_MAX_BYTE_NUM 32
-int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len)
-{
- int rc;
- int retry = 10;
- u8 buf[DDC_WRITE_MAX_BYTE_NUM];
- struct i2c_msg msgs[] = {
- {
- .addr = addr >> 1,
- .flags = 0,
- .len = 1,
- }
- };
-
- SDE_HDMI_DEBUG("Start DDC write");
- if (data_len > (DDC_WRITE_MAX_BYTE_NUM - 1)) {
- SDE_ERROR("%s: write size too big\n", __func__);
- return -ERANGE;
- }
-
- buf[0] = offset;
- memcpy(&buf[1], data, data_len);
- msgs[0].buf = buf;
- msgs[0].len = data_len + 1;
- retry:
- rc = i2c_transfer(hdmi->i2c, msgs, 1);
-
- retry--;
- if (rc == 1)
- rc = 0;
- else if (retry > 0)
- goto retry;
- else
- rc = -EIO;
-
- SDE_HDMI_DEBUG("End DDC write %d", rc);
-
- return rc;
-}
-
int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val)
{
int rc = 0;
@@ -1311,7 +1726,8 @@ int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val)
break;
}
- rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, data_buf, data_len);
+ rc = hdmi_ddc_read(hdmi, dev_addr, offset, data_buf,
+ data_len, true);
if (rc) {
SDE_ERROR("DDC Read failed for %d\n", data_type);
return rc;
@@ -1383,8 +1799,8 @@ int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val)
dev_addr = 0xA8;
data_len = 1;
offset = HDMI_SCDC_TMDS_CONFIG;
- rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, &read_val,
- data_len);
+ rc = hdmi_ddc_read(hdmi, dev_addr, offset, &read_val,
+ data_len, true);
if (rc) {
SDE_ERROR("scdc read failed\n");
return rc;
@@ -1408,7 +1824,8 @@ int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val)
return -EINVAL;
}
- rc = sde_hdmi_ddc_write(hdmi, dev_addr, offset, data_buf, data_len);
+ rc = hdmi_ddc_write(hdmi, dev_addr, offset, data_buf,
+ data_len, true);
if (rc) {
SDE_ERROR("DDC Read failed for %d\n", data_type);
return rc;
@@ -1442,14 +1859,114 @@ 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);
return rc;
}
+static void sde_hdmi_panel_set_hdr_infoframe(struct sde_hdmi *display,
+struct drm_msm_ext_panel_hdr_metadata *hdr_meta)
+{
+ u32 packet_payload = 0;
+ u32 packet_header = 0;
+ u32 packet_control = 0;
+ u32 const type_code = 0x87;
+ u32 const version = 0x01;
+ u32 const length = 0x1a;
+ u32 const descriptor_id = 0x00;
+ struct hdmi *hdmi;
+ struct drm_connector *connector;
+
+ if (!display || !hdr_meta) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ hdmi = display->ctrl.ctrl;
+ connector = display->ctrl.ctrl->connector;
+
+ if (!hdmi || !connector) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ if (!connector->hdr_supported) {
+ SDE_ERROR("Sink does not support HDR\n");
+ return;
+ }
+
+ /* Setup Packet header and payload */
+ packet_header = type_code | (version << 8) | (length << 16);
+ hdmi_write(hdmi, HDMI_GENERIC0_HDR, packet_header);
+
+ packet_payload = (hdr_meta->eotf << 8);
+ if (connector->hdr_metadata_type_one) {
+ packet_payload |= (descriptor_id << 16)
+ | (HDMI_GET_LSB(hdr_meta->display_primaries_x[0])
+ << 24);
+ hdmi_write(hdmi, HDMI_GENERIC0_0, packet_payload);
+ } else {
+ pr_debug("Metadata Type 1 not supported\n");
+ hdmi_write(hdmi, HDMI_GENERIC0_0, packet_payload);
+ goto enable_packet_control;
+ }
+
+ packet_payload =
+ (HDMI_GET_MSB(hdr_meta->display_primaries_x[0]))
+ | (HDMI_GET_LSB(hdr_meta->display_primaries_y[0]) << 8)
+ | (HDMI_GET_MSB(hdr_meta->display_primaries_y[0]) << 16)
+ | (HDMI_GET_LSB(hdr_meta->display_primaries_x[1]) << 24);
+ hdmi_write(hdmi, HDMI_GENERIC0_1, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(hdr_meta->display_primaries_x[1]))
+ | (HDMI_GET_LSB(hdr_meta->display_primaries_y[1]) << 8)
+ | (HDMI_GET_MSB(hdr_meta->display_primaries_y[1]) << 16)
+ | (HDMI_GET_LSB(hdr_meta->display_primaries_x[2]) << 24);
+ hdmi_write(hdmi, HDMI_GENERIC0_2, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(hdr_meta->display_primaries_x[2]))
+ | (HDMI_GET_LSB(hdr_meta->display_primaries_y[2]) << 8)
+ | (HDMI_GET_MSB(hdr_meta->display_primaries_y[2]) << 16)
+ | (HDMI_GET_LSB(hdr_meta->white_point_x) << 24);
+ hdmi_write(hdmi, HDMI_GENERIC0_3, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(hdr_meta->white_point_x))
+ | (HDMI_GET_LSB(hdr_meta->white_point_y) << 8)
+ | (HDMI_GET_MSB(hdr_meta->white_point_y) << 16)
+ | (HDMI_GET_LSB(hdr_meta->max_luminance) << 24);
+ hdmi_write(hdmi, HDMI_GENERIC0_4, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(hdr_meta->max_luminance))
+ | (HDMI_GET_LSB(hdr_meta->min_luminance) << 8)
+ | (HDMI_GET_MSB(hdr_meta->min_luminance) << 16)
+ | (HDMI_GET_LSB(hdr_meta->max_content_light_level) << 24);
+ hdmi_write(hdmi, HDMI_GENERIC0_5, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(hdr_meta->max_content_light_level))
+ | (HDMI_GET_LSB(hdr_meta->max_average_light_level) << 8)
+ | (HDMI_GET_MSB(hdr_meta->max_average_light_level) << 16);
+ hdmi_write(hdmi, HDMI_GENERIC0_6, packet_payload);
+
+enable_packet_control:
+ /*
+ * GENERIC0_LINE | GENERIC0_CONT | GENERIC0_SEND
+ * Setup HDMI TX generic packet control
+ * Enable this packet to transmit every frame
+ * Enable HDMI TX engine to transmit Generic packet 1
+ */
+ packet_control = hdmi_read(hdmi, HDMI_GEN_PKT_CTRL);
+ packet_control |= BIT(0) | BIT(1) | BIT(2) | BIT(16);
+ hdmi_write(hdmi, HDMI_GEN_PKT_CTRL, packet_control);
+}
+
int sde_hdmi_set_property(struct drm_connector *connector,
struct drm_connector_state *state,
int property_index,
@@ -1461,17 +1978,42 @@ int sde_hdmi_set_property(struct drm_connector *connector,
if (!connector || !display) {
SDE_ERROR("connector=%pK or display=%pK is NULL\n",
connector, display);
- return 0;
+ return -EINVAL;
}
SDE_DEBUG("\n");
- if (property_index == CONNECTOR_PROP_PLL_DELTA)
+ if (property_index == CONNECTOR_PROP_PLL_ENABLE)
+ rc = _sde_hdmi_enable_pll_update(display, value);
+ else if (property_index == CONNECTOR_PROP_PLL_DELTA)
rc = _sde_hdmi_update_pll_delta(display, value);
return rc;
}
+int sde_hdmi_get_property(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ int property_index,
+ uint64_t *value,
+ void *display)
+{
+ struct sde_hdmi *hdmi_display = display;
+ int rc = 0;
+
+ if (!connector || !hdmi_display) {
+ SDE_ERROR("connector=%pK or display=%pK is NULL\n",
+ connector, hdmi_display);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hdmi_display->display_lock);
+ if (property_index == CONNECTOR_PROP_PLL_ENABLE)
+ *value = hdmi_display->pll_update_enable ? 1 : 0;
+ mutex_unlock(&hdmi_display->display_lock);
+
+ return rc;
+}
+
u32 sde_hdmi_get_num_of_displays(void)
{
u32 count = 0;
@@ -1525,6 +2067,141 @@ int sde_hdmi_connector_pre_deinit(struct drm_connector *connector,
return 0;
}
+static void _sde_hdmi_get_tx_version(struct sde_hdmi *sde_hdmi)
+{
+ struct hdmi *hdmi = sde_hdmi->ctrl.ctrl;
+
+ sde_hdmi->hdmi_tx_version = hdmi_read(hdmi, REG_HDMI_VERSION);
+ sde_hdmi->hdmi_tx_major_version =
+ SDE_GET_MAJOR_VER(sde_hdmi->hdmi_tx_version);
+
+ switch (sde_hdmi->hdmi_tx_major_version) {
+ case (HDMI_TX_VERSION_3):
+ sde_hdmi->max_pclk_khz = HDMI_TX_3_MAX_PCLK_RATE;
+ break;
+ case (HDMI_TX_VERSION_4):
+ sde_hdmi->max_pclk_khz = HDMI_TX_4_MAX_PCLK_RATE;
+ break;
+ default:
+ sde_hdmi->max_pclk_khz = HDMI_DEFAULT_MAX_PCLK_RATE;
+ break;
+ }
+ SDE_DEBUG("sde_hdmi->hdmi_tx_version = 0x%x\n",
+ sde_hdmi->hdmi_tx_version);
+ SDE_DEBUG("sde_hdmi->hdmi_tx_major_version = 0x%x\n",
+ sde_hdmi->hdmi_tx_major_version);
+ SDE_DEBUG("sde_hdmi->max_pclk_khz = 0x%x\n",
+ sde_hdmi->max_pclk_khz);
+}
+
+static int sde_hdmi_tx_check_capability(struct sde_hdmi *sde_hdmi)
+{
+ u32 hdmi_disabled, hdcp_disabled, reg_val;
+ int ret = 0;
+ struct hdmi *hdmi = sde_hdmi->ctrl.ctrl;
+
+ /* check if hdmi and hdcp are disabled */
+ if (sde_hdmi->hdmi_tx_major_version < HDMI_TX_VERSION_4) {
+ hdcp_disabled = hdmi_qfprom_read(hdmi,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_LSB) & BIT(31);
+
+ hdmi_disabled = hdmi_qfprom_read(hdmi,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_MSB) & BIT(0);
+ } else {
+ reg_val = hdmi_qfprom_read(hdmi,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_LSB + QFPROM_RAW_VERSION_4);
+ hdcp_disabled = reg_val & BIT(12);
+
+ hdmi_disabled = reg_val & BIT(13);
+
+ reg_val = hdmi_qfprom_read(hdmi, SEC_CTRL_HW_VERSION);
+
+ SDE_DEBUG("SEC_CTRL_HW_VERSION reg_val = 0x%x\n", reg_val);
+ /*
+ * With HDCP enabled on capable hardware, check if HW
+ * or SW keys should be used.
+ */
+ if (!hdcp_disabled && (reg_val >= HDCP_SEL_MIN_SEC_VERSION)) {
+ reg_val = hdmi_qfprom_read(hdmi,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_MSB +
+ QFPROM_RAW_VERSION_4);
+
+ if (!(reg_val & BIT(23)))
+ sde_hdmi->hdcp1_use_sw_keys = true;
+ }
+ }
+
+ SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s>\n", __func__,
+ hdmi_disabled ? "OFF" : "ON",
+ hdcp_disabled ? "OFF" : "ON");
+
+ if (hdmi_disabled) {
+ DEV_ERR("%s: HDMI disabled\n", __func__);
+ ret = -ENODEV;
+ goto end;
+ }
+
+ sde_hdmi->hdcp14_present = !hdcp_disabled;
+
+ end:
+ return ret;
+} /* hdmi_tx_check_capability */
+
+static int _sde_hdmi_init_hdcp(struct sde_hdmi *hdmi_ctrl)
+{
+ struct sde_hdcp_init_data hdcp_init_data;
+ void *hdcp_data;
+ int rc = 0;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("sde_hdmi is NULL\n");
+ return -EINVAL;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+ hdcp_init_data.phy_addr = hdmi->mmio_phy_addr;
+ hdcp_init_data.core_io = &hdmi_ctrl->io[HDMI_TX_CORE_IO];
+ hdcp_init_data.qfprom_io = &hdmi_ctrl->io[HDMI_TX_QFPROM_IO];
+ hdcp_init_data.hdcp_io = &hdmi_ctrl->io[HDMI_TX_HDCP_IO];
+ hdcp_init_data.mutex = &hdmi_ctrl->hdcp_mutex;
+ hdcp_init_data.workq = hdmi->workq;
+ hdcp_init_data.notify_status = sde_hdmi_tx_hdcp_cb;
+ hdcp_init_data.cb_data = (void *)hdmi_ctrl;
+ hdcp_init_data.hdmi_tx_ver = hdmi_ctrl->hdmi_tx_major_version;
+ hdcp_init_data.sec_access = true;
+ hdcp_init_data.client_id = HDCP_CLIENT_HDMI;
+ hdcp_init_data.ddc_ctrl = &hdmi_ctrl->ddc_ctrl;
+
+ if (hdmi_ctrl->hdcp14_present) {
+ hdcp_data = sde_hdcp_1x_init(&hdcp_init_data);
+
+ if (IS_ERR_OR_NULL(hdcp_data)) {
+ DEV_ERR("%s: hdcp 1.4 init failed\n", __func__);
+ rc = -EINVAL;
+ kfree(hdcp_data);
+ goto end;
+ } else {
+ hdmi_ctrl->hdcp_feat_data[SDE_HDCP_1x] = hdcp_data;
+ SDE_HDMI_DEBUG("%s: HDCP 1.4 initialized\n", __func__);
+ }
+ }
+
+ hdcp_data = sde_hdmi_hdcp2p2_init(&hdcp_init_data);
+
+ if (IS_ERR_OR_NULL(hdcp_data)) {
+ DEV_ERR("%s: hdcp 2.2 init failed\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ } else {
+ hdmi_ctrl->hdcp_feat_data[SDE_HDCP_2P2] = hdcp_data;
+ SDE_HDMI_DEBUG("%s: HDCP 2.2 initialized\n", __func__);
+ }
+
+end:
+ return rc;
+}
+
int sde_hdmi_connector_post_init(struct drm_connector *connector,
void *info,
void *display)
@@ -1557,6 +2234,37 @@ int sde_hdmi_connector_post_init(struct drm_connector *connector,
if (rc)
SDE_ERROR("failed to enable HPD: %d\n", rc);
+ _sde_hdmi_get_tx_version(sde_hdmi);
+
+ sde_hdmi_tx_check_capability(sde_hdmi);
+
+ _sde_hdmi_init_hdcp(sde_hdmi);
+
+ return rc;
+}
+
+int sde_hdmi_start_hdcp(struct drm_connector *connector)
+{
+ int rc;
+ struct sde_connector *c_conn = to_sde_connector(connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!sde_hdmi_tx_is_hdcp_enabled(display))
+ return 0;
+
+ if (sde_hdmi_tx_is_encryption_set(display))
+ sde_hdmi_config_avmute(hdmi, true);
+
+ rc = display->hdcp_ops->authenticate(display->hdcp_data);
+ if (rc)
+ SDE_ERROR("%s: hdcp auth failed. rc=%d\n", __func__, rc);
+
return rc;
}
@@ -1595,6 +2303,17 @@ sde_hdmi_connector_detect(struct drm_connector *connector,
return status;
}
+int sde_hdmi_pre_kickoff(struct drm_connector *connector,
+ void *display,
+ struct msm_display_kickoff_params *params)
+{
+
+ sde_hdmi_panel_set_hdr_infoframe(display,
+ params->hdr_metadata);
+
+ return 0;
+}
+
int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
{
struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
@@ -1673,10 +2392,34 @@ int sde_hdmi_dev_deinit(struct sde_hdmi *display)
SDE_ERROR("Invalid params\n");
return -EINVAL;
}
+ if (display->hdcp_feat_data[SDE_HDCP_1x])
+ sde_hdcp_1x_deinit(display->hdcp_feat_data[SDE_HDCP_1x]);
+
+ if (display->hdcp_feat_data[SDE_HDCP_2P2])
+ sde_hdmi_hdcp2p2_deinit(display->hdcp_feat_data[SDE_HDCP_2P2]);
return 0;
}
+static int _sde_hdmi_cec_init(struct sde_hdmi *display)
+{
+ struct platform_device *pdev = display->pdev;
+
+ display->notifier = cec_notifier_get(&pdev->dev);
+ if (!display->notifier) {
+ SDE_ERROR("CEC notifier get failed\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void _sde_hdmi_cec_deinit(struct sde_hdmi *display)
+{
+ cec_notifier_set_phys_addr(display->notifier, CEC_PHYS_ADDR_INVALID);
+ cec_notifier_put(display->notifier);
+}
+
static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
{
int rc = 0;
@@ -1715,7 +2458,14 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
if (rc) {
SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n",
display->name, rc);
- goto error;
+ goto ext_error;
+ }
+
+ rc = _sde_hdmi_cec_init(display);
+ if (rc) {
+ SDE_ERROR("[%s]CEC init failed, rc=%d\n",
+ display->name, rc);
+ goto ext_error;
}
display->edid_ctrl = sde_edid_init();
@@ -1723,16 +2473,27 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
SDE_ERROR("[%s]sde edid init failed\n",
display->name);
rc = -ENOMEM;
- goto error;
+ goto cec_error;
}
display_ctrl = &display->ctrl;
display_ctrl->ctrl = priv->hdmi;
display->drm_dev = drm;
+ _sde_hdmi_map_regs(display, priv->hdmi);
+ _sde_hdmi_init_ddc(display, priv->hdmi);
+
+ display->enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+
+ INIT_DELAYED_WORK(&display->hdcp_cb_work,
+ sde_hdmi_tx_hdcp_cb_work);
+ mutex_init(&display->hdcp_mutex);
mutex_unlock(&display->display_lock);
return rc;
-error:
+
+cec_error:
+ (void)_sde_hdmi_cec_deinit(display);
+ext_error:
(void)_sde_hdmi_debugfs_deinit(display);
debug_error:
mutex_unlock(&display->display_lock);
@@ -1758,6 +2519,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master,
mutex_lock(&display->display_lock);
(void)_sde_hdmi_debugfs_deinit(display);
(void)sde_edid_deinit((void **)&display->edid_ctrl);
+ (void)_sde_hdmi_cec_deinit(display);
display->drm_dev = NULL;
mutex_unlock(&display->display_lock);
}
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
index 54506da4f9b0..6b9bcfec031b 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -20,12 +20,18 @@
#include <linux/debugfs.h>
#include <linux/of_device.h>
#include <linux/msm_ext_display.h>
+#include <linux/hdcp_qseecom.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
+#include <media/cec-notifier.h>
#include "hdmi.h"
-
+#include "sde_kms.h"
+#include "sde_connector.h"
+#include "msm_drv.h"
#include "sde_edid_parser.h"
+#include "sde_hdmi_util.h"
+#include "sde_hdcp.h"
#ifdef HDMI_DEBUG_ENABLE
#define SDE_HDMI_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
@@ -33,6 +39,10 @@
#define SDE_HDMI_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
#endif
+/* HW Revisions for different SDE targets */
+#define SDE_GET_MAJOR_VER(rev)((rev) >> 28)
+#define SDE_GET_MINOR_VER(rev)(((rev) >> 16) & 0xFFF)
+
/**
* struct sde_hdmi_info - defines hdmi display properties
* @display_type: Display type as defined by device tree.
@@ -68,6 +78,18 @@ struct sde_hdmi_ctrl {
u32 hdmi_ctrl_idx;
};
+enum hdmi_tx_io_type {
+ HDMI_TX_CORE_IO,
+ HDMI_TX_QFPROM_IO,
+ HDMI_TX_HDCP_IO,
+ HDMI_TX_MAX_IO
+};
+
+enum hdmi_tx_feature_type {
+ SDE_HDCP_1x,
+ SDE_HDCP_2P2
+};
+
/**
* struct sde_hdmi - hdmi display information
* @pdev: Pointer to platform device.
@@ -87,6 +109,8 @@ struct sde_hdmi_ctrl {
* @codec_ready: If audio codec is ready.
* @client_notify_pending: If there is client notification pending.
* @irq_domain: IRQ domain structure.
+ * @pll_update_enable: if it's allowed to update HDMI PLL ppm.
+ * @notifier: CEC notifider to convey physical address information.
* @root: Debug fs root entry.
*/
struct sde_hdmi {
@@ -97,7 +121,7 @@ struct sde_hdmi {
const char *display_type;
struct list_head list;
struct mutex display_lock;
-
+ struct mutex hdcp_mutex;
struct sde_hdmi_ctrl ctrl;
struct platform_device *ext_pdev;
@@ -110,13 +134,36 @@ struct sde_hdmi {
struct drm_display_mode mode;
bool connected;
bool is_tpg_enabled;
-
+ u32 hdmi_tx_version;
+ u32 hdmi_tx_major_version;
+ u32 max_pclk_khz;
+ bool hdcp1_use_sw_keys;
+ u32 hdcp14_present;
+ u32 hdcp22_present;
+ u8 hdcp_status;
+ u32 enc_lvl;
+ bool auth_state;
+ bool sink_hdcp22_support;
+ bool src_hdcp22_support;
+
+ /*hold final data
+ *based on hdcp support
+ */
+ void *hdcp_data;
+ /*hold hdcp init data*/
+ void *hdcp_feat_data[2];
+ struct sde_hdcp_ops *hdcp_ops;
+ struct sde_hdmi_tx_ddc_ctrl ddc_ctrl;
struct work_struct hpd_work;
bool codec_ready;
bool client_notify_pending;
struct irq_domain *irq_domain;
+ struct cec_notifier *notifier;
+ bool pll_update_enable;
+ struct delayed_work hdcp_cb_work;
+ struct dss_io_data io[HDMI_TX_MAX_IO];
/* DEBUG FS */
struct dentry *root;
};
@@ -141,6 +188,11 @@ enum hdmi_tx_scdc_access_type {
#define HDMI_KHZ_TO_HZ 1000
#define HDMI_MHZ_TO_HZ 1000000
+
+/* Maximum pixel clock rates for hdmi tx */
+#define HDMI_DEFAULT_MAX_PCLK_RATE 148500
+#define HDMI_TX_3_MAX_PCLK_RATE 297000
+#define HDMI_TX_4_MAX_PCLK_RATE 600000
/**
* hdmi_tx_ddc_timer_type() - hdmi DDC timer functionalities.
*/
@@ -295,6 +347,22 @@ int sde_hdmi_set_property(struct drm_connector *connector,
void *display);
/**
+ * sde_hdmi_get_property() - get the connector properties
+ * @connector: Handle to the connector.
+ * @state: Handle to the connector state.
+ * @property_index: property index.
+ * @value: property value.
+ * @display: Handle to the display.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_get_property(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ int property_index,
+ uint64_t *value,
+ void *display);
+
+/**
* sde_hdmi_bridge_init() - init sde hdmi bridge
* @hdmi: Handle to the hdmi.
*
@@ -312,32 +380,6 @@ struct drm_bridge *sde_hdmi_bridge_init(struct hdmi *hdmi);
void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on);
/**
- * sde_hdmi_ddc_read() - common hdmi ddc read API.
- * @hdmi: Handle to the hdmi.
- * @addr: Command address.
- * @offset: Command offset.
- * @data: Data buffer for read back.
- * @data_len: Data buffer length.
- *
- * Return: error code.
- */
-int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len);
-
-/**
- * sde_hdmi_ddc_write() - common hdmi ddc write API.
- * @hdmi: Handle to the hdmi.
- * @addr: Command address.
- * @offset: Command offset.
- * @data: Data buffer for write.
- * @data_len: Data buffer length.
- *
- * Return: error code.
- */
-int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len);
-
-/**
* sde_hdmi_scdc_read() - hdmi 2.0 ddc read API.
* @hdmi: Handle to the hdmi.
* @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type.
@@ -403,6 +445,24 @@ void sde_hdmi_notify_clients(struct sde_hdmi *display, bool connected);
void sde_hdmi_ack_state(struct drm_connector *connector,
enum drm_connector_status status);
+bool sde_hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl);
+int sde_hdmi_start_hdcp(struct drm_connector *connector);
+void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl);
+
+
+/*
+ * sde_hdmi_pre_kickoff - program kickoff-time features
+ * @display: Pointer to private display structure
+ * @params: Parameters for kickoff-time programming
+ * Returns: Zero on success
+ */
+int sde_hdmi_pre_kickoff(struct drm_connector *connector,
+ void *display,
+ struct msm_display_kickoff_params *params);
+
#else /*#ifdef CONFIG_DRM_SDE_HDMI*/
static inline u32 sde_hdmi_get_num_of_displays(void)
@@ -461,12 +521,42 @@ static inline int sde_hdmi_dev_deinit(struct sde_hdmi *display)
return 0;
}
+bool hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
static inline int sde_hdmi_drm_init(struct sde_hdmi *display,
struct drm_encoder *enc)
{
return 0;
}
+int sde_hdmi_start_hdcp(struct drm_connector *connector)
+{
+ return 0;
+}
+
+void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl)
+{
+
+}
+
static inline int sde_hdmi_drm_deinit(struct sde_hdmi *display)
{
return 0;
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
index 48a3a9316a41..d6213dc0a4aa 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
@@ -355,37 +355,3 @@ void sde_hdmi_audio_off(struct hdmi *hdmi)
SDE_DEBUG("HDMI Audio: Disabled\n");
}
-int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set)
-{
- u32 av_mute_status;
- bool av_pkt_en = false;
-
- if (!hdmi) {
- SDE_ERROR("invalid HDMI Ctrl\n");
- return -ENODEV;
- }
-
- av_mute_status = hdmi_read(hdmi, HDMI_GC);
-
- if (set) {
- if (!(av_mute_status & BIT(0))) {
- hdmi_write(hdmi, HDMI_GC, av_mute_status | BIT(0));
- av_pkt_en = true;
- }
- } else {
- if (av_mute_status & BIT(0)) {
- hdmi_write(hdmi, HDMI_GC, av_mute_status & ~BIT(0));
- av_pkt_en = true;
- }
- }
-
- /* Enable AV Mute tranmission here */
- if (av_pkt_en)
- hdmi_write(hdmi, HDMI_VBI_PKT_CTRL,
- hdmi_read(hdmi, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5)));
-
- SDE_DEBUG("AVMUTE %s\n", set ? "set" : "cleared");
-
- return 0;
-}
-
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
index 34268aaedfc0..24d2320683e4 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
@@ -22,6 +22,71 @@
#include "sde_hdmi.h"
#include "hdmi.h"
+/*
+ * Add these register definitions to support the latest chipsets. These
+ * are derived from hdmi.xml.h and are going to be replaced by a chipset
+ * based mask approach.
+ */
+#define SDE_HDMI_ACTIVE_HSYNC_START__MASK 0x00001fff
+static inline uint32_t SDE_HDMI_ACTIVE_HSYNC_START(uint32_t val)
+{
+ return ((val) << HDMI_ACTIVE_HSYNC_START__SHIFT) &
+ SDE_HDMI_ACTIVE_HSYNC_START__MASK;
+}
+#define SDE_HDMI_ACTIVE_HSYNC_END__MASK 0x1fff0000
+static inline uint32_t SDE_HDMI_ACTIVE_HSYNC_END(uint32_t val)
+{
+ return ((val) << HDMI_ACTIVE_HSYNC_END__SHIFT) &
+ SDE_HDMI_ACTIVE_HSYNC_END__MASK;
+}
+
+#define SDE_HDMI_ACTIVE_VSYNC_START__MASK 0x00001fff
+static inline uint32_t SDE_HDMI_ACTIVE_VSYNC_START(uint32_t val)
+{
+ return ((val) << HDMI_ACTIVE_VSYNC_START__SHIFT) &
+ SDE_HDMI_ACTIVE_VSYNC_START__MASK;
+}
+#define SDE_HDMI_ACTIVE_VSYNC_END__MASK 0x1fff0000
+static inline uint32_t SDE_HDMI_ACTIVE_VSYNC_END(uint32_t val)
+{
+ return ((val) << HDMI_ACTIVE_VSYNC_END__SHIFT) &
+ SDE_HDMI_ACTIVE_VSYNC_END__MASK;
+}
+
+#define SDE_HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00001fff
+static inline uint32_t SDE_HDMI_VSYNC_ACTIVE_F2_START(uint32_t val)
+{
+ return ((val) << HDMI_VSYNC_ACTIVE_F2_START__SHIFT) &
+ SDE_HDMI_VSYNC_ACTIVE_F2_START__MASK;
+}
+#define SDE_HDMI_VSYNC_ACTIVE_F2_END__MASK 0x1fff0000
+static inline uint32_t SDE_HDMI_VSYNC_ACTIVE_F2_END(uint32_t val)
+{
+ return ((val) << HDMI_VSYNC_ACTIVE_F2_END__SHIFT) &
+ SDE_HDMI_VSYNC_ACTIVE_F2_END__MASK;
+}
+
+#define SDE_HDMI_TOTAL_H_TOTAL__MASK 0x00001fff
+static inline uint32_t SDE_HDMI_TOTAL_H_TOTAL(uint32_t val)
+{
+ return ((val) << HDMI_TOTAL_H_TOTAL__SHIFT) &
+ SDE_HDMI_TOTAL_H_TOTAL__MASK;
+}
+
+#define SDE_HDMI_TOTAL_V_TOTAL__MASK 0x1fff0000
+static inline uint32_t SDE_HDMI_TOTAL_V_TOTAL(uint32_t val)
+{
+ return ((val) << HDMI_TOTAL_V_TOTAL__SHIFT) &
+ SDE_HDMI_TOTAL_V_TOTAL__MASK;
+}
+
+#define SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00001fff
+static inline uint32_t SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val)
+{
+ return ((val) << HDMI_VSYNC_TOTAL_F2_V_TOTAL__SHIFT) &
+ SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK;
+}
+
struct sde_hdmi_bridge {
struct drm_bridge base;
struct hdmi *hdmi;
@@ -32,8 +97,7 @@ struct sde_hdmi_bridge {
#define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04
#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000
#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200
-/* default hsyncs for 4k@60 for 200ms */
-#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571
+
/* for AVI program */
#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \
@@ -177,39 +241,22 @@ static int _sde_hdmi_bridge_scrambler_ddc_check_status(struct hdmi *hdmi)
return rc;
}
-static void _sde_hdmi_bridge_scrambler_ddc_reset(struct hdmi *hdmi)
-{
- u32 reg_val;
-
- /* clear ack and disable interrupts */
- reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1);
- hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val);
-
- /* Reset DDC timers */
- reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
- hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
-
- reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
- reg_val &= ~BIT(0);
- hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
-}
-
-static void _sde_hdmi_bridge_scrambler_ddc_disable(struct hdmi *hdmi)
-{
- u32 reg_val;
-
- _sde_hdmi_bridge_scrambler_ddc_reset(hdmi);
- /* Disable HW DDC access to RxStatus register */
- reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL);
- reg_val &= ~(BIT(8) | BIT(9));
- hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val);
-}
-
static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi,
u32 timeout_hsync)
{
u32 reg_val;
int rc;
+ struct sde_connector *c_conn;
+ struct drm_connector *connector = NULL;
+ struct sde_hdmi *display;
+
+ if (!hdmi) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+ connector = hdmi->connector;
+ c_conn = to_sde_connector(hdmi->connector);
+ display = (struct sde_hdmi *)c_conn->display;
_sde_hdmi_bridge_ddc_clear_irq(hdmi, "scrambler");
@@ -243,7 +290,7 @@ static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi,
if (rc)
SDE_ERROR("scrambling ddc error %d\n", rc);
- _sde_hdmi_bridge_scrambler_ddc_disable(hdmi);
+ _sde_hdmi_scrambler_ddc_disable((void *)display);
return rc;
}
@@ -269,20 +316,6 @@ static int _sde_hdmi_bridge_setup_ddc_timers(struct hdmi *hdmi,
return 0;
}
-static inline int _sde_hdmi_bridge_get_timeout_in_hysnc(
- struct drm_display_mode *mode, u32 timeout_ms)
-{
- /*
- * pixel clock = h_total * v_total * fps
- * 1 sec = pixel clock number of pixels are transmitted.
- * time taken by one line (h_total) = 1s / (v_total * fps).
- * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps))
- * = (time_ms * clock) / h_total
- */
-
- return (timeout_ms * mode->clock / mode->htotal);
-}
-
static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
struct drm_display_mode *mode)
{
@@ -291,14 +324,17 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
u32 reg_val = 0;
u32 tmds_clock_ratio = 0;
bool scrambler_on = false;
-
+ struct sde_connector *c_conn;
struct drm_connector *connector = NULL;
+ struct sde_hdmi *display;
if (!hdmi || !mode) {
SDE_ERROR("invalid input\n");
return -EINVAL;
}
connector = hdmi->connector;
+ c_conn = to_sde_connector(hdmi->connector);
+ display = (struct sde_hdmi *)c_conn->display;
/* Read HDMI version */
reg_val = hdmi_read(hdmi, REG_HDMI_VERSION);
@@ -328,7 +364,6 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
}
reg_val = hdmi_read(hdmi, REG_HDMI_CTRL);
- reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */
reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */
hdmi_write(hdmi, REG_HDMI_CTRL, reg_val);
@@ -345,9 +380,10 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
* status bit on the sink. Sink should set this bit
* with in 200ms after scrambler is enabled.
*/
- timeout_hsync = _sde_hdmi_bridge_get_timeout_in_hysnc(
- mode,
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)display,
HDMI_TX_SCRAMBLER_TIMEOUT_MSEC);
+
if (timeout_hsync <= 0) {
SDE_ERROR("err in timeout hsync calc\n");
timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
@@ -360,7 +396,6 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
} else {
sde_hdmi_scdc_write(hdmi, HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0);
reg_val = hdmi_read(hdmi, REG_HDMI_CTRL);
- reg_val &= ~BIT(31); /* Disable Update DATAPATH_MODE */
reg_val &= ~BIT(28); /* Unset SCRAMBLER_EN bit */
hdmi_write(hdmi, REG_HDMI_CTRL, reg_val);
}
@@ -398,12 +433,77 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
mutex_unlock(&display->display_lock);
}
+static void sde_hdmi_update_hdcp_info(struct drm_connector *connector)
+{
+ void *fd = NULL;
+ struct sde_hdcp_ops *ops = NULL;
+ struct sde_connector *c_conn = to_sde_connector(connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+
+ if (!display) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ /* check first if hdcp2p2 is supported */
+ fd = display->hdcp_feat_data[SDE_HDCP_2P2];
+ if (fd)
+ ops = sde_hdmi_hdcp2p2_start(fd);
+
+ /* If ops is true, sink supports hdcp */
+ if (ops)
+ display->sink_hdcp22_support = true;
+
+ if (ops && ops->feature_supported)
+ display->hdcp22_present = ops->feature_supported(fd);
+ else
+ display->hdcp22_present = false;
+
+ /* if hdcp22_present is true, src supports hdcp 2p2 */
+ if (display->hdcp22_present)
+ display->src_hdcp22_support = true;
+
+ if (!display->hdcp22_present) {
+ if (display->hdcp1_use_sw_keys) {
+ display->hdcp14_present =
+ hdcp1_check_if_supported_load_app();
+ }
+ if (display->hdcp14_present) {
+ fd = display->hdcp_feat_data[SDE_HDCP_1x];
+ if (fd)
+ ops = sde_hdcp_1x_start(fd);
+ }
+ }
+
+ /* update internal data about hdcp */
+ display->hdcp_data = fd;
+ display->hdcp_ops = ops;
+}
+
static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge)
{
+ struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
+ struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
+
+ /* need to update hdcp info here to ensure right HDCP support*/
+ sde_hdmi_update_hdcp_info(hdmi->connector);
+
+ /* start HDCP authentication */
+ sde_hdmi_start_hdcp(hdmi->connector);
}
static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge)
{
+ struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
+ struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
+ struct sde_connector *c_conn = to_sde_connector(hdmi->connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+
+ mutex_lock(&display->display_lock);
+
+ display->pll_update_enable = false;
+
+ mutex_unlock(&display->display_lock);
}
static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
@@ -416,8 +516,8 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
sde_hdmi_notify_clients(display, display->connected);
- if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported)
- hdmi_hdcp_ctrl_off(hdmi->hdcp_ctrl);
+ if (sde_hdmi_tx_is_hdcp_enabled(display))
+ sde_hdmi_hdcp_off(display);
sde_hdmi_audio_off(hdmi);
@@ -584,28 +684,28 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
mode->htotal, mode->vtotal, hstart, hend, vstart, vend);
hdmi_write(hdmi, REG_HDMI_TOTAL,
- HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
- HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));
+ SDE_HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
+ SDE_HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));
hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,
- HDMI_ACTIVE_HSYNC_START(hstart) |
- HDMI_ACTIVE_HSYNC_END(hend));
+ SDE_HDMI_ACTIVE_HSYNC_START(hstart) |
+ SDE_HDMI_ACTIVE_HSYNC_END(hend));
hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC,
- HDMI_ACTIVE_VSYNC_START(vstart) |
- HDMI_ACTIVE_VSYNC_END(vend));
+ SDE_HDMI_ACTIVE_VSYNC_START(vstart) |
+ SDE_HDMI_ACTIVE_VSYNC_END(vend));
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
- HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));
+ SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));
hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
- HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |
- HDMI_VSYNC_ACTIVE_F2_END(vend + 1));
+ SDE_HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |
+ SDE_HDMI_VSYNC_ACTIVE_F2_END(vend + 1));
} else {
hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
- HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));
+ SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));
hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
- HDMI_VSYNC_ACTIVE_F2_START(0) |
- HDMI_VSYNC_ACTIVE_F2_END(0));
+ SDE_HDMI_VSYNC_ACTIVE_F2_START(0) |
+ SDE_HDMI_VSYNC_ACTIVE_F2_END(0));
}
frame_ctrl = 0;
@@ -631,9 +731,9 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
_sde_hdmi_bridge_set_spd_infoframe(hdmi, mode);
DRM_DEBUG("hdmi setup info frame\n");
}
- _sde_hdmi_bridge_setup_scrambler(hdmi, mode);
_sde_hdmi_save_mode(hdmi, mode);
+ _sde_hdmi_bridge_setup_scrambler(hdmi, mode);
}
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c
new file mode 100644
index 000000000000..1e673440f399
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c
@@ -0,0 +1,994 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/kthread.h>
+
+#include <linux/hdcp_qseecom.h>
+#include "sde_hdcp.h"
+#include "video/msm_hdmi_hdcp_mgr.h"
+#include "sde_hdmi_util.h"
+
+/*
+ * Defined addresses and offsets of standard HDCP 2.2 sink registers
+ * for DDC, as defined in HDCP 2.2 spec section 2.14 table 2.7
+ */
+#define HDCP_SINK_DDC_SLAVE_ADDR 0x74 /* Sink DDC slave address */
+#define HDCP_SINK_DDC_HDCP2_VERSION 0x50 /* Does sink support HDCP2.2 */
+#define HDCP_SINK_DDC_HDCP2_WRITE_MESSAGE 0x60 /* HDCP Tx writes here */
+#define HDCP_SINK_DDC_HDCP2_RXSTATUS 0x70 /* RxStatus, 2 bytes */
+#define HDCP_SINK_DDC_HDCP2_READ_MESSAGE 0x80 /* HDCP Tx reads here */
+
+#define HDCP2P2_DEFAULT_TIMEOUT 500
+
+/*
+ * HDCP 2.2 encryption requires the data encryption block that is present in
+ * HDMI controller version 4.0.0 and above
+ */
+#define MIN_HDMI_TX_MAJOR_VERSION 4
+
+enum sde_hdmi_hdcp2p2_sink_status {
+ SINK_DISCONNECTED,
+ SINK_CONNECTED
+};
+
+enum sde_hdmi_auth_status {
+ HDMI_HDCP_AUTH_STATUS_FAILURE,
+ HDMI_HDCP_AUTH_STATUS_SUCCESS
+};
+
+struct sde_hdmi_hdcp2p2_ctrl {
+ atomic_t auth_state;
+ enum sde_hdmi_hdcp2p2_sink_status sink_status; /* Is sink connected */
+ struct sde_hdcp_init_data init_data; /* Feature data from HDMI drv */
+ struct mutex mutex; /* mutex to protect access to ctrl */
+ struct mutex msg_lock; /* mutex to protect access to msg buffer */
+ struct mutex wakeup_mutex; /* mutex to protect access to wakeup call*/
+ struct sde_hdcp_ops *ops;
+ void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */
+ struct hdcp_txmtr_ops *lib; /* Ops for driver to call into TZ */
+
+ enum hdmi_hdcp_wakeup_cmd wakeup_cmd;
+ enum sde_hdmi_auth_status auth_status;
+ char *send_msg_buf;
+ uint32_t send_msg_len;
+ uint32_t timeout;
+ uint32_t timeout_left;
+
+ struct task_struct *thread;
+ struct kthread_worker worker;
+ struct kthread_work status;
+ struct kthread_work auth;
+ struct kthread_work send_msg;
+ struct kthread_work recv_msg;
+ struct kthread_work link;
+ struct kthread_work poll;
+};
+
+static int sde_hdmi_hdcp2p2_auth(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_send_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_recv_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_auth_status(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static int sde_hdmi_hdcp2p2_link_check(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+
+static bool sde_hdcp2p2_is_valid_state(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE)
+ return true;
+
+ if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
+ return true;
+
+ return false;
+}
+
+static int sde_hdmi_hdcp2p2_copy_buf(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ struct hdmi_hdcp_wakeup_data *data)
+{
+ mutex_lock(&ctrl->msg_lock);
+
+ if (!data->send_msg_len) {
+ mutex_unlock(&ctrl->msg_lock);
+ return 0;
+ }
+
+ ctrl->send_msg_len = data->send_msg_len;
+
+ kzfree(ctrl->send_msg_buf);
+
+ ctrl->send_msg_buf = kzalloc(data->send_msg_len, GFP_KERNEL);
+
+ if (!ctrl->send_msg_buf) {
+ mutex_unlock(&ctrl->msg_lock);
+ return -ENOMEM;
+ }
+
+ memcpy(ctrl->send_msg_buf, data->send_msg_buf, ctrl->send_msg_len);
+
+ mutex_unlock(&ctrl->msg_lock);
+
+ return 0;
+}
+
+static int sde_hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ if (!data) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ctrl = data->context;
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&ctrl->wakeup_mutex);
+
+ SDE_HDCP_DEBUG("cmd: %s, timeout %dms\n",
+ hdmi_hdcp_cmd_to_str(data->cmd),
+ data->timeout);
+
+ ctrl->wakeup_cmd = data->cmd;
+
+ if (data->timeout)
+ ctrl->timeout = data->timeout * 2;
+ else
+ ctrl->timeout = HDCP2P2_DEFAULT_TIMEOUT;
+
+ if (!sde_hdcp2p2_is_valid_state(ctrl)) {
+ SDE_ERROR("invalid state\n");
+ goto exit;
+ }
+
+ if (sde_hdmi_hdcp2p2_copy_buf(ctrl, data))
+ goto exit;
+
+ if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS)
+ ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_SUCCESS;
+ else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED)
+ ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_FAILURE;
+
+ switch (ctrl->wakeup_cmd) {
+ case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE:
+ queue_kthread_work(&ctrl->worker, &ctrl->send_msg);
+ break;
+ case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE:
+ queue_kthread_work(&ctrl->worker, &ctrl->recv_msg);
+ break;
+ case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS:
+ case HDMI_HDCP_WKUP_CMD_STATUS_FAILED:
+ queue_kthread_work(&ctrl->worker, &ctrl->status);
+ break;
+ case HDMI_HDCP_WKUP_CMD_LINK_POLL:
+ queue_kthread_work(&ctrl->worker, &ctrl->poll);
+ break;
+ case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
+ queue_kthread_work(&ctrl->worker, &ctrl->auth);
+ break;
+ default:
+ SDE_ERROR("invalid wakeup command %d\n", ctrl->wakeup_cmd);
+ }
+exit:
+ mutex_unlock(&ctrl->wakeup_mutex);
+ return 0;
+}
+
+static int sde_hdmi_hdcp2p2_wakeup_lib(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ struct hdcp_lib_wakeup_data *data)
+{
+ int rc = 0;
+
+ if (ctrl && ctrl->lib && ctrl->lib->wakeup &&
+ data && (data->cmd != HDCP_LIB_WKUP_CMD_INVALID)) {
+ rc = ctrl->lib->wakeup(data);
+ if (rc)
+ SDE_ERROR("error sending %s to lib\n",
+ hdcp_lib_cmd_to_str(data->cmd));
+ }
+
+ return rc;
+}
+
+static void sde_hdmi_hdcp2p2_reset(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ ctrl->sink_status = SINK_DISCONNECTED;
+ atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
+}
+
+static void sde_hdmi_hdcp2p2_off(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE};
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ sde_hdmi_hdcp2p2_reset(ctrl);
+
+ flush_kthread_worker(&ctrl->worker);
+
+ sde_hdmi_hdcp2p2_ddc_disable((void *)ctrl->init_data.cb_data);
+
+ cdata.context = input;
+ sde_hdmi_hdcp2p2_wakeup(&cdata);
+}
+
+static int sde_hdmi_hdcp2p2_authenticate(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = input;
+ struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE};
+ u32 regval;
+ int rc = 0;
+
+ /* Enable authentication success interrupt */
+ regval = DSS_REG_R(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2);
+ regval |= BIT(1) | BIT(2);
+
+ DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, regval);
+
+ flush_kthread_worker(&ctrl->worker);
+
+ ctrl->sink_status = SINK_CONNECTED;
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING);
+
+ /* make sure ddc is idle before starting hdcp 2.2 authentication */
+ _sde_hdmi_scrambler_ddc_disable((void *)ctrl->init_data.cb_data);
+ sde_hdmi_hdcp2p2_ddc_disable((void *)ctrl->init_data.cb_data);
+
+ cdata.context = input;
+ sde_hdmi_hdcp2p2_wakeup(&cdata);
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_reauthenticate(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ sde_hdmi_hdcp2p2_reset(ctrl);
+
+ return sde_hdmi_hdcp2p2_authenticate(input);
+}
+
+static void sde_hdmi_hdcp2p2_min_level_change(void *client_ctx,
+int min_enc_lvl)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl =
+ (struct sde_hdmi_hdcp2p2_ctrl *)client_ctx;
+ struct hdcp_lib_wakeup_data cdata = {
+ HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE};
+ bool enc_notify = true;
+ enum sde_hdcp_states enc_lvl;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ switch (min_enc_lvl) {
+ case 0:
+ enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+ break;
+ case 1:
+ enc_lvl = HDCP_STATE_AUTH_ENC_1X;
+ break;
+ case 2:
+ enc_lvl = HDCP_STATE_AUTH_ENC_2P2;
+ break;
+ default:
+ enc_notify = false;
+ }
+
+ SDE_HDCP_DEBUG("enc level changed %d\n", min_enc_lvl);
+
+ cdata.context = ctrl->lib_ctx;
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+
+ if (enc_notify && ctrl->init_data.notify_status)
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl);
+}
+
+static void sde_hdmi_hdcp2p2_auth_failed(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
+
+ sde_hdmi_hdcp2p2_ddc_disable(ctrl->init_data.cb_data);
+
+ /* notify hdmi tx about HDCP failure */
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data,
+ HDCP_STATE_AUTH_FAIL);
+}
+
+static int sde_hdmi_hdcp2p2_ddc_rd_message(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *buf, int size, u32 timeout)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ return -EINVAL;
+ }
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_READ_MESSAGE;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = size;
+ ddc_data->request_len = size;
+ ddc_data->retry = 0;
+ ddc_data->hard_timeout = timeout;
+ ddc_data->what = "HDCP2ReadMessage";
+
+ rc = sde_hdmi_ddc_read(ctrl->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("Cannot read HDCP message register\n");
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_ddc_wt_message(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *buf, size_t size)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_WRITE_MESSAGE;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = size;
+ ddc_data->hard_timeout = ctrl->timeout;
+ ddc_data->what = "HDCP2WriteMessage";
+
+ rc = sde_hdmi_ddc_write((void *)ctrl->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("Cannot write HDCP message register\n");
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_read_version(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *hdcp2version)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_VERSION;
+ ddc_data->data_buf = hdcp2version;
+ ddc_data->data_len = 1;
+ ddc_data->request_len = 1;
+ ddc_data->retry = 1;
+ ddc_data->what = "HDCP2Version";
+
+ rc = sde_hdmi_ddc_read((void *)ctrl->init_data.cb_data);
+ if (rc) {
+ SDE_ERROR("Cannot read HDCP2Version register");
+ return rc;
+ }
+
+ SDE_HDCP_DEBUG("Read HDCP2Version as %u\n", *hdcp2version);
+ return rc;
+}
+
+static bool sde_hdmi_hdcp2p2_feature_supported(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = input;
+ struct hdcp_txmtr_ops *lib = NULL;
+ bool supported = false;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ goto end;
+ }
+
+ lib = ctrl->lib;
+ if (!lib) {
+ SDE_ERROR("invalid lib ops data\n");
+ goto end;
+ }
+
+ if (lib->feature_supported) {
+ supported = lib->feature_supported(
+ ctrl->lib_ctx);
+ }
+
+end:
+ return supported;
+}
+
+static void sde_hdmi_hdcp2p2_send_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ int rc = 0;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ uint32_t msglen;
+ char *msg = NULL;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ goto exit;
+ }
+
+ mutex_lock(&ctrl->msg_lock);
+ msglen = ctrl->send_msg_len;
+
+ if (!msglen) {
+ mutex_unlock(&ctrl->msg_lock);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ msg = kzalloc(msglen, GFP_KERNEL);
+ if (!msg) {
+ mutex_unlock(&ctrl->msg_lock);
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(msg, ctrl->send_msg_buf, msglen);
+ mutex_unlock(&ctrl->msg_lock);
+
+ /* Forward the message to the sink */
+ rc = sde_hdmi_hdcp2p2_ddc_wt_message(ctrl,
+ msg, (size_t)msglen);
+ if (rc) {
+ SDE_ERROR("Error sending msg to sink %d\n", rc);
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED;
+ } else {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS;
+ cdata.timeout = ctrl->timeout_left;
+ }
+exit:
+ kfree(msg);
+
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+}
+
+static void sde_hdmi_hdcp2p2_send_msg_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, send_msg);
+
+ sde_hdmi_hdcp2p2_send_msg(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_link_cb(void *data)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = data;
+
+ if (!ctrl) {
+ SDE_HDCP_DEBUG("invalid input\n");
+ return;
+ }
+
+ if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
+ queue_kthread_work(&ctrl->worker, &ctrl->link);
+}
+
+static void sde_hdmi_hdcp2p2_recv_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ int timeout_hsync = 0, rc = 0;
+ char *recvd_msg_buf = NULL;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ goto exit;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ if (!ddc_ctrl) {
+ pr_err("invalid ddc ctrl\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ memset(ddc_data, 0, sizeof(*ddc_data));
+
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)ctrl->init_data.cb_data, ctrl->timeout);
+
+ if (timeout_hsync <= 0) {
+ SDE_ERROR("err in timeout hsync calc\n");
+ timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
+ }
+
+ SDE_HDCP_DEBUG("timeout for rxstatus %dms, %d hsync\n",
+ ctrl->timeout, timeout_hsync);
+
+ ddc_data->intr_mask = RXSTATUS_MESSAGE_SIZE | RXSTATUS_REAUTH_REQ;
+ ddc_data->timeout_ms = ctrl->timeout;
+ ddc_data->timeout_hsync = timeout_hsync;
+ ddc_data->periodic_timer_hsync = timeout_hsync / 20;
+ ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;
+ ddc_data->wait = true;
+
+ rc = sde_hdmi_hdcp2p2_read_rxstatus(ctrl->init_data.cb_data);
+ if (rc) {
+ SDE_ERROR("error reading rxstatus %d\n", rc);
+ goto exit;
+ }
+
+ if (ddc_data->reauth_req) {
+ ddc_data->reauth_req = false;
+
+ SDE_HDCP_DEBUG("reauth triggered by sink\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ SDE_HDCP_DEBUG("timeout left after rxstatus %dms, msg size %d\n",
+ ctrl->timeout_left, ddc_data->message_size);
+
+ if (!ddc_data->message_size) {
+ SDE_ERROR("recvd invalid message size\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL);
+ if (!recvd_msg_buf) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ rc = sde_hdmi_hdcp2p2_ddc_rd_message(ctrl, recvd_msg_buf,
+ ddc_data->message_size, ctrl->timeout_left);
+ if (rc) {
+ SDE_ERROR("error reading message %d\n", rc);
+ goto exit;
+ }
+
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS;
+ cdata.recvd_msg_buf = recvd_msg_buf;
+ cdata.recvd_msg_len = ddc_data->message_size;
+ cdata.timeout = ctrl->timeout_left;
+exit:
+ if (rc == -ETIMEDOUT)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT;
+ else if (rc)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED;
+
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ kfree(recvd_msg_buf);
+}
+
+static void sde_hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, recv_msg);
+
+ sde_hdmi_hdcp2p2_recv_msg(ctrl);
+}
+
+static int sde_hdmi_hdcp2p2_link_check(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ int timeout_hsync;
+ int ret;
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+
+ if (!ddc_ctrl)
+ return -EINVAL;
+
+ sde_hdmi_ddc_config(ctrl->init_data.cb_data);
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)ctrl->init_data.cb_data,
+ jiffies_to_msecs(HZ / 2));
+
+ if (timeout_hsync <= 0) {
+ SDE_ERROR("err in timeout hsync calc\n");
+ timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
+ }
+ SDE_HDCP_DEBUG("timeout for rxstatus %d hsyncs\n", timeout_hsync);
+
+ ddc_data->intr_mask = RXSTATUS_READY | RXSTATUS_MESSAGE_SIZE |
+ RXSTATUS_REAUTH_REQ;
+ ddc_data->timeout_hsync = timeout_hsync;
+ ddc_data->periodic_timer_hsync = timeout_hsync;
+ ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;
+ ddc_data->link_cb = sde_hdmi_hdcp2p2_link_cb;
+ ddc_data->link_data = ctrl;
+
+ ret = sde_hdmi_hdcp2p2_read_rxstatus((void *)ctrl->init_data.cb_data);
+ return ret;
+}
+
+static void sde_hdmi_hdcp2p2_poll_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, poll);
+
+ sde_hdmi_hdcp2p2_link_check(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_auth_status(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ return;
+ }
+
+ if (ctrl->auth_status == HDMI_HDCP_AUTH_STATUS_SUCCESS) {
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data,
+ HDCP_STATE_AUTHENTICATED);
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATED);
+ } else {
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+ }
+}
+
+static void sde_hdmi_hdcp2p2_auth_status_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, status);
+
+ sde_hdmi_hdcp2p2_auth_status(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_link_work(struct kthread_work *work)
+{
+ int rc = 0;
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, link);
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ char *recvd_msg_buf = NULL;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ if (!ddc_ctrl) {
+ rc = -EINVAL;
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+
+ if (ddc_data->reauth_req) {
+ SDE_HDCP_DEBUG("reauth triggered by sink\n");
+
+ ddc_data->reauth_req = false;
+ rc = -ENOLINK;
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ if (ddc_data->ready && ddc_data->message_size) {
+ SDE_HDCP_DEBUG("topology changed. rxstatus msg size %d\n",
+ ddc_data->message_size);
+
+ ddc_data->ready = false;
+
+ recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL);
+ if (!recvd_msg_buf) {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ rc = sde_hdmi_hdcp2p2_ddc_rd_message(ctrl, recvd_msg_buf,
+ ddc_data->message_size, HDCP2P2_DEFAULT_TIMEOUT);
+ if (rc) {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ SDE_ERROR("error reading message %d\n", rc);
+ } else {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS;
+ cdata.recvd_msg_buf = recvd_msg_buf;
+ cdata.recvd_msg_len = ddc_data->message_size;
+ }
+
+ ddc_data->message_size = 0;
+ }
+exit:
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ kfree(recvd_msg_buf);
+
+ if (rc) {
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+ return;
+ }
+}
+
+static int sde_hdmi_hdcp2p2_auth(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ int rc = 0;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_START;
+ else
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+
+ rc = sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ if (rc)
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+
+ return rc;
+}
+
+static void sde_hdmi_hdcp2p2_auth_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, auth);
+
+ sde_hdmi_hdcp2p2_auth(ctrl);
+}
+
+void sde_hdmi_hdcp2p2_deinit(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ cdata.context = ctrl->lib_ctx;
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+
+ kthread_stop(ctrl->thread);
+
+ mutex_destroy(&ctrl->mutex);
+ mutex_destroy(&ctrl->msg_lock);
+ mutex_destroy(&ctrl->wakeup_mutex);
+ kfree(ctrl);
+}
+
+void *sde_hdmi_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
+{
+ int rc;
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ static struct sde_hdcp_ops ops = {
+ .reauthenticate = sde_hdmi_hdcp2p2_reauthenticate,
+ .authenticate = sde_hdmi_hdcp2p2_authenticate,
+ .feature_supported = sde_hdmi_hdcp2p2_feature_supported,
+ .off = sde_hdmi_hdcp2p2_off
+ };
+
+ static struct hdcp_client_ops client_ops = {
+ .wakeup = sde_hdmi_hdcp2p2_wakeup,
+ .notify_lvl_change = sde_hdmi_hdcp2p2_min_level_change,
+ };
+
+ static struct hdcp_txmtr_ops txmtr_ops;
+ struct hdcp_register_data register_data;
+
+ SDE_HDCP_DEBUG("HDCP2P2 feature initialization\n");
+
+ if (!init_data || !init_data->core_io || !init_data->mutex ||
+ !init_data->ddc_ctrl || !init_data->notify_status ||
+ !init_data->workq || !init_data->cb_data) {
+ SDE_ERROR("invalid input\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (init_data->hdmi_tx_ver < MIN_HDMI_TX_MAJOR_VERSION) {
+ SDE_ERROR("HDMI Tx does not support HDCP 2.2\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return ERR_PTR(-ENOMEM);
+
+ ctrl->init_data = *init_data;
+ ctrl->lib = &txmtr_ops;
+
+ ctrl->sink_status = SINK_DISCONNECTED;
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
+
+ ctrl->ops = &ops;
+ mutex_init(&ctrl->mutex);
+ mutex_init(&ctrl->msg_lock);
+ mutex_init(&ctrl->wakeup_mutex);
+
+ register_data.hdcp_ctx = &ctrl->lib_ctx;
+ register_data.client_ops = &client_ops;
+ register_data.txmtr_ops = &txmtr_ops;
+ register_data.device_type = HDCP_TXMTR_HDMI;
+ register_data.client_ctx = ctrl;
+
+ rc = hdcp_library_register(&register_data);
+ if (rc) {
+ SDE_ERROR("Unable to register with HDCP 2.2 library\n");
+ goto error;
+ }
+
+ init_kthread_worker(&ctrl->worker);
+
+ init_kthread_work(&ctrl->auth, sde_hdmi_hdcp2p2_auth_work);
+ init_kthread_work(&ctrl->send_msg, sde_hdmi_hdcp2p2_send_msg_work);
+ init_kthread_work(&ctrl->recv_msg, sde_hdmi_hdcp2p2_recv_msg_work);
+ init_kthread_work(&ctrl->status, sde_hdmi_hdcp2p2_auth_status_work);
+ init_kthread_work(&ctrl->link, sde_hdmi_hdcp2p2_link_work);
+ init_kthread_work(&ctrl->poll, sde_hdmi_hdcp2p2_poll_work);
+
+ ctrl->thread = kthread_run(kthread_worker_fn,
+ &ctrl->worker, "hdmi_hdcp2p2");
+
+ if (IS_ERR(ctrl->thread)) {
+ SDE_ERROR("unable to start hdcp2p2 thread\n");
+ rc = PTR_ERR(ctrl->thread);
+ ctrl->thread = NULL;
+ goto error;
+ }
+
+ return ctrl;
+error:
+ kfree(ctrl);
+ return ERR_PTR(rc);
+}
+
+static bool sde_hdmi_hdcp2p2_supported(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ u8 hdcp2version = 0;
+ int rc = sde_hdmi_hdcp2p2_read_version(ctrl, &hdcp2version);
+
+ if (rc)
+ goto error;
+
+ if (hdcp2version & BIT(2)) {
+ SDE_HDCP_DEBUG("Sink is HDCP 2.2 capable\n");
+ return true;
+ }
+
+error:
+ SDE_HDCP_DEBUG("Sink is not HDCP 2.2 capable\n");
+ return false;
+}
+
+struct sde_hdcp_ops *sde_hdmi_hdcp2p2_start(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ SDE_HDCP_DEBUG("Checking sink capability\n");
+ if (sde_hdmi_hdcp2p2_supported(ctrl))
+ return ctrl->ops;
+ else
+ return NULL;
+
+}
+
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
new file mode 100644
index 000000000000..a7887d2c84b0
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
@@ -0,0 +1,827 @@
+/* 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/slab.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/iopoll.h>
+#include <linux/types.h>
+#include <linux/switch.h>
+#include <linux/gcd.h>
+
+#include "drm_edid.h"
+#include "sde_kms.h"
+#include "sde_hdmi.h"
+#include "sde_hdmi_regs.h"
+#include "hdmi.h"
+
+#define HDMI_SEC_TO_MS 1000
+#define HDMI_MS_TO_US 1000
+#define HDMI_SEC_TO_US (HDMI_SEC_TO_MS * HDMI_MS_TO_US)
+#define HDMI_KHZ_TO_HZ 1000
+#define HDMI_BUSY_WAIT_DELAY_US 100
+
+static void sde_hdmi_hdcp2p2_ddc_clear_status(struct sde_hdmi *display)
+{
+ u32 reg_val;
+ struct hdmi *hdmi;
+
+ if (!display) {
+ pr_err("invalid ddc ctrl\n");
+ return;
+ }
+ hdmi = display->ctrl.ctrl;
+ /* check for errors and clear status */
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_STATUS);
+
+ if (reg_val & BIT(4)) {
+ pr_debug("ddc aborted\n");
+ reg_val |= BIT(5);
+ }
+
+ if (reg_val & BIT(8)) {
+ pr_debug("timed out\n");
+ reg_val |= BIT(9);
+ }
+
+ if (reg_val & BIT(12)) {
+ pr_debug("NACK0\n");
+ reg_val |= BIT(13);
+ }
+
+ if (reg_val & BIT(14)) {
+ pr_debug("NACK1\n");
+ reg_val |= BIT(15);
+ }
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_STATUS, reg_val);
+}
+
+/**
+ * sde_hdmi_dump_regs - utility to dump HDMI regs
+ * @hdmi_display: Pointer to private display handle
+ * Return : void
+ */
+
+void sde_hdmi_dump_regs(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi;
+ int i;
+ u32 addr_off = 0;
+ u32 len = 0;
+
+ if (!display) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ if (!hdmi->power_on || !display->connected) {
+ SDE_ERROR("HDMI display is not ready\n");
+ return;
+ }
+
+ len = hdmi->mmio_len;
+
+ if (len % 16)
+ len += 16;
+ len /= 16;
+
+ pr_info("HDMI CORE regs\n");
+ for (i = 0; i < len; i++) {
+ u32 x0, x4, x8, xc;
+
+ x0 = hdmi_read(hdmi, addr_off+0x0);
+ x4 = hdmi_read(hdmi, addr_off+0x4);
+ x8 = hdmi_read(hdmi, addr_off+0x8);
+ xc = hdmi_read(hdmi, addr_off+0xc);
+
+ pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+ xc);
+
+ addr_off += 16;
+ }
+}
+
+int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display)
+{
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *data;
+ u32 intr0, intr2, intr5;
+ u32 msg_size;
+ int rc = 0;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi;
+
+ ddc_ctrl = &display->ddc_ctrl;
+ data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ intr0 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL0);
+ intr2 = hdmi_read(hdmi, HDMI_HDCP_INT_CTRL2);
+ intr5 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+
+ pr_debug("intr0: 0x%x, intr2: 0x%x, intr5: 0x%x\n",
+ intr0, intr2, intr5);
+
+ /* check if encryption is enabled */
+ if (intr2 & BIT(0)) {
+ /*
+ * ack encryption ready interrupt.
+ * disable encryption ready interrupt.
+ * enable encryption not ready interrupt.
+ */
+ intr2 &= ~BIT(2);
+ intr2 |= BIT(1) | BIT(6);
+
+ pr_info("HDCP 2.2 Encryption enabled\n");
+ data->encryption_ready = true;
+ }
+
+ /* check if encryption is disabled */
+ if (intr2 & BIT(4)) {
+ /*
+ * ack encryption not ready interrupt.
+ * disable encryption not ready interrupt.
+ * enable encryption ready interrupt.
+ */
+ intr2 &= ~BIT(6);
+ intr2 |= BIT(5) | BIT(2);
+
+ pr_info("HDCP 2.2 Encryption disabled\n");
+ data->encryption_ready = false;
+ }
+
+ hdmi_write(hdmi, HDMI_HDCP_INT_CTRL2, intr2);
+
+ /* get the message size bits 29:20 */
+ msg_size = (intr0 & (0x3FF << 20)) >> 20;
+
+ if (msg_size) {
+ /* ack and disable message size interrupt */
+ intr0 |= BIT(30);
+ intr0 &= ~BIT(31);
+
+ data->message_size = msg_size;
+ }
+
+ /* check and disable ready interrupt */
+ if (intr0 & BIT(16)) {
+ /* ack ready/not ready interrupt */
+ intr0 |= BIT(17);
+ intr0 &= ~BIT(18);
+ pr_debug("got ready interrupt\n");
+ data->ready = true;
+ }
+
+ /* check for reauth req interrupt */
+ if (intr0 & BIT(12)) {
+ /* ack and disable reauth req interrupt */
+ intr0 |= BIT(13);
+ intr0 &= ~BIT(14);
+ pr_err("got reauth interrupt\n");
+ data->reauth_req = true;
+ }
+
+ /* check for ddc fail interrupt */
+ if (intr0 & BIT(8)) {
+ /* ack ddc fail interrupt */
+ intr0 |= BIT(9);
+ pr_err("got ddc fail interrupt\n");
+ data->ddc_max_retries_fail = true;
+ }
+
+ /* check for ddc done interrupt */
+ if (intr0 & BIT(4)) {
+ /* ack ddc done interrupt */
+ intr0 |= BIT(5);
+ pr_debug("got ddc done interrupt\n");
+ data->ddc_done = true;
+ }
+
+ /* check for ddc read req interrupt */
+ if (intr0 & BIT(0)) {
+ /* ack read req interrupt */
+ intr0 |= BIT(1);
+
+ data->ddc_read_req = true;
+ }
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, intr0);
+
+ if (intr5 & BIT(0)) {
+ pr_err("RXSTATUS_DDC_REQ_TIMEOUT\n");
+
+ /* ack and disable timeout interrupt */
+ intr5 |= BIT(1);
+ intr5 &= ~BIT(2);
+
+ data->ddc_timeout = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, intr5);
+
+ if (data->message_size || data->ready || data->reauth_req) {
+ if (data->wait) {
+ complete(&ddc_ctrl->rx_status_done);
+ } else if (data->link_cb && data->link_data) {
+ data->link_cb(data->link_data);
+ } else {
+ pr_err("new msg/reauth not handled\n");
+ rc = -EINVAL;
+ }
+ }
+
+ sde_hdmi_hdcp2p2_ddc_clear_status(display);
+
+ return rc;
+}
+
+int sde_hdmi_ddc_scrambling_isr(void *hdmi_display)
+{
+
+ bool scrambler_timer_off = false;
+ u32 intr2, intr5;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi;
+
+
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ intr2 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL2);
+ intr5 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+
+ pr_debug("intr2: 0x%x, intr5: 0x%x\n", intr2, intr5);
+
+ if (intr2 & BIT(12)) {
+ pr_err("SCRAMBLER_STATUS_NOT\n");
+
+ intr2 |= BIT(14);
+ scrambler_timer_off = true;
+ }
+
+ if (intr2 & BIT(8)) {
+ pr_err("SCRAMBLER_STATUS_DDC_FAILED\n");
+
+ intr2 |= BIT(9);
+
+ scrambler_timer_off = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL2, intr2);
+
+ if (intr5 & BIT(8)) {
+ pr_err("SCRAMBLER_STATUS_DDC_REQ_TIMEOUT\n");
+ intr5 |= BIT(9);
+ intr5 &= ~BIT(10);
+ scrambler_timer_off = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, intr5);
+
+ if (scrambler_timer_off)
+ _sde_hdmi_scrambler_ddc_disable((void *)display);
+
+ return 0;
+}
+
+static int sde_hdmi_ddc_read_retry(struct sde_hdmi *display)
+{
+ int status;
+ int busy_wait_us;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct hdmi *hdmi;
+
+ if (!display) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ hdmi = display->ctrl.ctrl;
+ ddc_ctrl = &display->ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (!ddc_data->data_buf) {
+ status = -EINVAL;
+ SDE_ERROR("%s: invalid buf\n", ddc_data->what);
+ goto error;
+ }
+
+ if (ddc_data->retry < 0) {
+ SDE_ERROR("invalid no. of retries %d\n", ddc_data->retry);
+ status = -EINVAL;
+ goto error;
+ }
+
+ do {
+ if (ddc_data->hard_timeout) {
+ HDMI_UTIL_DEBUG("using hard_timeout %dms\n",
+ ddc_data->hard_timeout);
+
+ busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US;
+ hdmi->use_hard_timeout = true;
+ hdmi->busy_wait_us = busy_wait_us;
+ }
+
+ /* Calling upstream ddc read method */
+ status = hdmi_ddc_read(hdmi, ddc_data->dev_addr,
+ ddc_data->offset,
+ ddc_data->data_buf, ddc_data->request_len,
+ false);
+
+ if (ddc_data->hard_timeout)
+ ddc_data->timeout_left = hdmi->timeout_count;
+
+
+ if (ddc_data->hard_timeout && !hdmi->timeout_count) {
+ HDMI_UTIL_DEBUG("%s: timedout\n", ddc_data->what);
+ status = -ETIMEDOUT;
+ }
+
+ } while (status && ddc_data->retry--);
+
+ if (status) {
+ HDMI_UTIL_ERROR("%s: failed status = %d\n",
+ ddc_data->what, status);
+ goto error;
+ }
+
+ HDMI_UTIL_DEBUG("%s: success\n", ddc_data->what);
+
+error:
+ return status;
+} /* sde_hdmi_ddc_read_retry */
+
+int sde_hdmi_ddc_read(void *cb_data)
+{
+ int rc = 0;
+ int retry;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi *display = (struct sde_hdmi *)cb_data;
+
+ if (!display) {
+ SDE_ERROR("invalid ddc ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = &display->ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+ retry = ddc_data->retry;
+
+ rc = sde_hdmi_ddc_read_retry(display);
+ if (!rc)
+ return rc;
+
+ if (ddc_data->retry_align) {
+ ddc_data->retry = retry;
+
+ ddc_data->request_len = 32 * ((ddc_data->data_len + 31) / 32);
+ rc = sde_hdmi_ddc_read_retry(display);
+ }
+
+ return rc;
+} /* hdmi_ddc_read */
+
+int sde_hdmi_ddc_write(void *cb_data)
+{
+ int status;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ int busy_wait_us;
+ struct hdmi *hdmi;
+ struct sde_hdmi *display = (struct sde_hdmi *)cb_data;
+
+ if (!display) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ hdmi = display->ctrl.ctrl;
+ ddc_ctrl = &display->ddc_ctrl;
+
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (!ddc_data->data_buf) {
+ status = -EINVAL;
+ SDE_ERROR("%s: invalid buf\n", ddc_data->what);
+ goto error;
+ }
+
+ if (ddc_data->retry < 0) {
+ SDE_ERROR("invalid no. of retries %d\n", ddc_data->retry);
+ status = -EINVAL;
+ goto error;
+ }
+
+ do {
+ if (ddc_data->hard_timeout) {
+ busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US;
+ hdmi->use_hard_timeout = true;
+ hdmi->busy_wait_us = busy_wait_us;
+ }
+
+ status = hdmi_ddc_write(hdmi,
+ ddc_data->dev_addr, ddc_data->offset,
+ ddc_data->data_buf, ddc_data->data_len,
+ false);
+
+ if (ddc_data->hard_timeout)
+ ddc_data->timeout_left = hdmi->timeout_count;
+
+ if (ddc_data->hard_timeout && !hdmi->timeout_count) {
+ HDMI_UTIL_ERROR("%s timout\n", ddc_data->what);
+ status = -ETIMEDOUT;
+ }
+
+ } while (status && ddc_data->retry--);
+
+ if (status) {
+ HDMI_UTIL_ERROR("%s: failed status = %d\n",
+ ddc_data->what, status);
+ goto error;
+ }
+
+ HDMI_UTIL_DEBUG("%s: success\n", ddc_data->what);
+error:
+ return status;
+} /* hdmi_ddc_write */
+
+bool sde_hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl)
+{
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ return (hdmi_ctrl->hdcp14_present || hdmi_ctrl->hdcp22_present) &&
+ hdmi_ctrl->hdcp_ops;
+}
+
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl)
+{
+ bool enc_en = true;
+ u32 reg_val;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ goto end;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ reg_val = hdmi_read(hdmi, HDMI_HDCP_CTRL2);
+ if ((reg_val & BIT(0)) && (reg_val & BIT(1)))
+ goto end;
+
+ if (hdmi_read(hdmi, HDMI_CTRL) & BIT(2))
+ goto end;
+
+ return false;
+
+end:
+ return enc_en;
+} /* sde_hdmi_tx_is_encryption_set */
+
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl)
+{
+ bool ret;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ switch (hdmi_ctrl->enc_lvl) {
+ case HDCP_STATE_AUTH_ENC_NONE:
+ ret = true;
+ break;
+ case HDCP_STATE_AUTH_ENC_1X:
+ ret = sde_hdmi_tx_is_hdcp_enabled(hdmi_ctrl) &&
+ hdmi_ctrl->auth_state;
+ break;
+ case HDCP_STATE_AUTH_ENC_2P2:
+ ret = hdmi_ctrl->hdcp22_present &&
+ hdmi_ctrl->auth_state;
+ break;
+ default:
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl)
+{
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ return hdmi_ctrl->connected && hdmi->power_on;
+}
+
+int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set)
+{
+ u32 av_mute_status;
+ bool av_pkt_en = false;
+
+ if (!hdmi) {
+ SDE_ERROR("invalid HDMI Ctrl\n");
+ return -ENODEV;
+ }
+
+ av_mute_status = hdmi_read(hdmi, HDMI_GC);
+
+ if (set) {
+ if (!(av_mute_status & BIT(0))) {
+ hdmi_write(hdmi, HDMI_GC, av_mute_status | BIT(0));
+ av_pkt_en = true;
+ }
+ } else {
+ if (av_mute_status & BIT(0)) {
+ hdmi_write(hdmi, HDMI_GC, av_mute_status & ~BIT(0));
+ av_pkt_en = true;
+ }
+ }
+
+ /* Enable AV Mute tranmission here */
+ if (av_pkt_en)
+ hdmi_write(hdmi, HDMI_VBI_PKT_CTRL,
+ hdmi_read(hdmi, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5)));
+
+ pr_info("AVMUTE %s\n", set ? "set" : "cleared");
+
+ return 0;
+}
+
+int _sde_hdmi_get_timeout_in_hysnc(void *hdmi_display, u32 timeout_ms)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct drm_display_mode mode = display->mode;
+ /*
+ * pixel clock = h_total * v_total * fps
+ * 1 sec = pixel clock number of pixels are transmitted.
+ * time taken by one line (h_total) = 1s / (v_total * fps).
+ * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps))
+ * = (time_ms * clock) / h_total
+ */
+
+ return (timeout_ms * mode.clock / mode.htotal);
+}
+
+static void sde_hdmi_hdcp2p2_ddc_reset(struct sde_hdmi *hdmi_ctrl)
+{
+ u32 reg_val;
+ struct hdmi *hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ /*
+ * Clear acks for DDC_REQ, DDC_DONE, DDC_FAILED, RXSTATUS_READY,
+ * RXSTATUS_MSG_SIZE
+ */
+ reg_val = BIT(30) | BIT(17) | BIT(13) | BIT(9) | BIT(5) | BIT(1);
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, reg_val);
+ /* Reset DDC timers */
+ reg_val = BIT(0) | hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ reg_val &= ~BIT(0);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+}
+
+void sde_hdmi_hdcp2p2_ddc_disable(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ u32 reg_val;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ sde_hdmi_hdcp2p2_ddc_reset(display);
+
+ /* Disable HW DDC access to RxStatus register */
+ reg_val = hdmi_read(hdmi, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+
+ hdmi_write(hdmi, HDMI_HW_DDC_CTRL, reg_val);
+}
+
+static void _sde_hdmi_scrambler_ddc_reset(struct hdmi *hdmi)
+{
+ u32 reg_val;
+
+ /* clear ack and disable interrupts */
+ reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1);
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val);
+
+ /* Reset DDC timers */
+ reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
+
+ reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
+ reg_val &= ~BIT(0);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
+}
+
+void _sde_hdmi_scrambler_ddc_disable(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ u32 reg_val;
+
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ _sde_hdmi_scrambler_ddc_reset(hdmi);
+ /* Disable HW DDC access to RxStatus register */
+ reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(8) | BIT(9));
+ hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val);
+}
+
+void sde_hdmi_ddc_config(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+ hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
+ HDMI_DDC_SPEED_THRESHOLD(2) |
+ HDMI_DDC_SPEED_PRESCALE(10));
+
+ hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
+ HDMI_DDC_SETUP_TIMEOUT(0xff));
+
+ /* enable reference timer for 19us */
+ hdmi_write(hdmi, REG_HDMI_DDC_REF,
+ HDMI_DDC_REF_REFTIMER_ENABLE |
+ HDMI_DDC_REF_REFTIMER(19));
+}
+
+int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display)
+{
+ u32 reg_val;
+ u32 intr_en_mask;
+ u32 timeout;
+ u32 timer;
+ int rc = 0;
+ int busy_wait_us;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *data;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ u32 rem;
+
+ if (!hdmi) {
+ pr_err("Invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = &display->ddc_ctrl;
+ data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ if (!data) {
+ pr_err("Invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ rc = ddc_clear_irq(hdmi);
+ if (rc) {
+ pr_err("DDC clear irq failed\n");
+ return rc;
+ }
+ intr_en_mask = data->intr_mask;
+ intr_en_mask |= BIT(HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK);
+
+ /* Disable short read for now, sinks don't support it */
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ reg_val |= BIT(4);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+ /*
+ * Setup the DDC timers for HDMI_HDCP2P2_DDC_TIMER_CTRL1 and
+ * HDMI_HDCP2P2_DDC_TIMER_CTRL2.
+ * Following are the timers:
+ * 1. DDC_REQUEST_TIMER: Timeout in hsyncs in which to wait for the
+ * HDCP 2.2 sink to respond to an RxStatus request
+ * 2. DDC_URGENT_TIMER: Time period in hsyncs to issue an urgent flag
+ * when an RxStatus DDC request is made but not accepted by I2C
+ * engine
+ * 3. DDC_TIMEOUT_TIMER: Timeout in hsyncs which starts counting when
+ * a request is made and stops when it is accepted by DDC arbiter
+ */
+
+ timeout = data->timeout_hsync;
+ timer = data->periodic_timer_hsync;
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_TIMER_CTRL, timer);
+ /* Set both urgent and hw-timeout fields to the same value */
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_TIMER_CTRL2,
+ (timeout << 16 | timeout));
+ /* enable interrupts */
+ reg_val = intr_en_mask;
+ /* Clear interrupt status bits */
+ reg_val |= intr_en_mask >> 1;
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, reg_val);
+ reg_val = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+ /* clear and enable RxStatus read timeout */
+ reg_val |= BIT(2) | BIT(1);
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, reg_val);
+ /*
+ * Enable hardware DDC access to RxStatus register
+ *
+ * HDMI_HW_DDC_CTRL:Bits 1:0 (RXSTATUS_DDC_ENABLE) read like this:
+ *
+ * 0 = disable HW controlled DDC access to RxStatus
+ * 1 = automatic on when HDCP 2.2 is authenticated and loop based on
+ * request timer (i.e. the hardware will loop automatically)
+ * 2 = force on and loop based on request timer (hardware will loop)
+ * 3 = enable by sw trigger and loop until interrupt is generated for
+ * RxStatus.reauth_req, RxStatus.ready or RxStatus.message_Size.
+ *
+ * Depending on the value of ddc_data::poll_sink, we make the decision
+ * to use either SW_TRIGGER(3) (poll_sink = false) which means that the
+ * hardware will poll sink and generate interrupt when sink responds,
+ * or use AUTOMATIC_LOOP(1) (poll_sink = true) which will poll the sink
+ * based on request timer
+ */
+
+ reg_val = hdmi_read(hdmi, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+
+ busy_wait_us = data->timeout_ms * HDMI_MS_TO_US;
+
+ /* read method: HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER */
+ reg_val |= BIT(1) | BIT(0);
+ hdmi_write(hdmi, HDMI_HW_DDC_CTRL, reg_val);
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_SW_TRIGGER, 1);
+ if (data->wait) {
+ reinit_completion(&ddc_ctrl->rx_status_done);
+ rem = wait_for_completion_timeout(&ddc_ctrl->rx_status_done,
+ HZ);
+ data->timeout_left = jiffies_to_msecs(rem);
+
+ if (!data->timeout_left) {
+ pr_err("sw ddc rxstatus timeout\n");
+ rc = -ETIMEDOUT;
+ }
+ sde_hdmi_hdcp2p2_ddc_disable((void *)display);
+ }
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
new file mode 100644
index 000000000000..8b69dd31f637
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SDE_HDMI_UTIL_H_
+#define _SDE_HDMI_UTIL_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+#include <linux/msm_ext_display.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "hdmi.h"
+#include "sde_kms.h"
+#include "sde_connector.h"
+#include "msm_drv.h"
+#include "sde_hdmi_regs.h"
+
+#ifdef HDMI_UTIL_DEBUG_ENABLE
+#define HDMI_UTIL_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
+#else
+#define HDMI_UTIL_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
+#endif
+
+#define HDMI_UTIL_ERROR(fmt, args...) SDE_ERROR(fmt, ##args)
+
+/*
+ * Offsets in HDMI_DDC_INT_CTRL0 register
+ *
+ * The HDMI_DDC_INT_CTRL0 register is intended for HDCP 2.2 RxStatus
+ * register manipulation. It reads like this:
+ *
+ * Bit 31: RXSTATUS_MESSAGE_SIZE_MASK (1 = generate interrupt when size > 0)
+ * Bit 30: RXSTATUS_MESSAGE_SIZE_ACK (1 = Acknowledge message size intr)
+ * Bits 29-20: RXSTATUS_MESSAGE_SIZE (Actual size of message available)
+ * Bits 19-18: RXSTATUS_READY_MASK (1 = generate interrupt when ready = 1
+ * 2 = generate interrupt when ready = 0)
+ * Bit 17: RXSTATUS_READY_ACK (1 = Acknowledge ready bit interrupt)
+ * Bit 16: RXSTATUS_READY (1 = Rxstatus ready bit read is 1)
+ * Bit 15: RXSTATUS_READY_NOT (1 = Rxstatus ready bit read is 0)
+ * Bit 14: RXSTATUS_REAUTH_REQ_MASK (1 = generate interrupt when reauth is
+ * requested by sink)
+ * Bit 13: RXSTATUS_REAUTH_REQ_ACK (1 = Acknowledge Reauth req interrupt)
+ * Bit 12: RXSTATUS_REAUTH_REQ (1 = Rxstatus reauth req bit read is 1)
+ * Bit 10: RXSTATUS_DDC_FAILED_MASK (1 = generate interrupt when DDC
+ * tranasaction fails)
+ * Bit 9: RXSTATUS_DDC_FAILED_ACK (1 = Acknowledge ddc failure interrupt)
+ * Bit 8: RXSTATUS_DDC_FAILED (1 = DDC transaction failed)
+ * Bit 6: RXSTATUS_DDC_DONE_MASK (1 = generate interrupt when DDC
+ * transaction completes)
+ * Bit 5: RXSTATUS_DDC_DONE_ACK (1 = Acknowledge ddc done interrupt)
+ * Bit 4: RXSTATUS_DDC_DONE (1 = DDC transaction is done)
+ * Bit 2: RXSTATUS_DDC_REQ_MASK (1 = generate interrupt when DDC Read
+ * request for RXstatus is made)
+ * Bit 1: RXSTATUS_DDC_REQ_ACK (1 = Acknowledge Rxstatus read interrupt)
+ * Bit 0: RXSTATUS_DDC_REQ (1 = RXStatus DDC read request is made)
+ *
+ */
+
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_SHIFT 20
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK 0x3ff00000
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_ACK_SHIFT 30
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_INTR_SHIFT 31
+
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_SHIFT 12
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_MASK 1
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_ACK_SHIFT 13
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_INTR_SHIFT 14
+
+#define HDCP2P2_RXSTATUS_READY_SHIFT 16
+#define HDCP2P2_RXSTATUS_READY_MASK 1
+#define HDCP2P2_RXSTATUS_READY_ACK_SHIFT 17
+#define HDCP2P2_RXSTATUS_READY_INTR_SHIFT 18
+#define HDCP2P2_RXSTATUS_READY_INTR_MASK 18
+
+#define HDCP2P2_RXSTATUS_DDC_FAILED_SHIFT 8
+#define HDCP2P2_RXSTATUS_DDC_FAILED_ACKSHIFT 9
+#define HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK 10
+#define HDCP2P2_RXSTATUS_DDC_DONE 6
+
+/* default hsyncs for 4k@60 for 200ms */
+#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571
+
+#define HDMI_GET_MSB(x)(x >> 8)
+#define HDMI_GET_LSB(x)(x & 0xff)
+
+/*
+ * Bits 1:0 in HDMI_HW_DDC_CTRL that dictate how the HDCP 2.2 RxStatus will be
+ * read by the hardware
+ */
+#define HDCP2P2_RXSTATUS_HW_DDC_DISABLE 0
+#define HDCP2P2_RXSTATUS_HW_DDC_AUTOMATIC_LOOP 1
+#define HDCP2P2_RXSTATUS_HW_DDC_FORCE_LOOP 2
+#define HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER 3
+
+struct sde_hdmi_tx_ddc_data {
+ char *what;
+ u8 *data_buf;
+ u32 data_len;
+ u32 dev_addr;
+ u32 offset;
+ u32 request_len;
+ u32 retry_align;
+ u32 hard_timeout;
+ u32 timeout_left;
+ int retry;
+};
+
+enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask {
+ RXSTATUS_MESSAGE_SIZE = BIT(31),
+ RXSTATUS_READY = BIT(18),
+ RXSTATUS_REAUTH_REQ = BIT(14),
+};
+
+struct sde_hdmi_tx_hdcp2p2_ddc_data {
+ enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask intr_mask;
+ u32 timeout_ms;
+ u32 timeout_hsync;
+ u32 periodic_timer_hsync;
+ u32 timeout_left;
+ u32 read_method;
+ u32 message_size;
+ bool encryption_ready;
+ bool ready;
+ bool reauth_req;
+ bool ddc_max_retries_fail;
+ bool ddc_done;
+ bool ddc_read_req;
+ bool ddc_timeout;
+ bool wait;
+ int irq_wait_count;
+ void (*link_cb)(void *data);
+ void *link_data;
+};
+
+struct sde_hdmi_tx_ddc_ctrl {
+ struct completion rx_status_done;
+ struct dss_io_data *io;
+ struct sde_hdmi_tx_ddc_data ddc_data;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data sde_hdcp2p2_ddc_data;
+};
+
+/* DDC */
+int sde_hdmi_ddc_write(void *cb_data);
+int sde_hdmi_ddc_read(void *cb_data);
+int sde_hdmi_ddc_scrambling_isr(void *hdmi_display);
+int _sde_hdmi_get_timeout_in_hysnc(void *hdmi_display, u32 timeout_ms);
+void _sde_hdmi_scrambler_ddc_disable(void *hdmi_display);
+void sde_hdmi_hdcp2p2_ddc_disable(void *hdmi_display);
+int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display);
+void sde_hdmi_ddc_config(void *hdmi_display);
+int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display);
+void sde_hdmi_dump_regs(void *hdmi_display);
+#endif /* _SDE_HDMI_UTIL_H_ */
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 7915562057d6..7d660ba56594 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -95,7 +95,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
struct hdmi_platform_config *config = pdev->dev.platform_data;
struct hdmi *hdmi = NULL;
struct resource *res;
- int i, ret;
+ int i, ret = 0;
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi) {
@@ -119,9 +119,19 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
}
}
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, config->mmio_name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find ctrl resource\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ hdmi->mmio_len = (u32)resource_size(res);
hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI");
if (IS_ERR(hdmi->mmio)) {
ret = PTR_ERR(hdmi->mmio);
+ dev_info(&pdev->dev, "can't map hdmi resource\n");
+ hdmi->mmio = NULL;
goto fail;
}
@@ -130,13 +140,39 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
config->mmio_name);
hdmi->mmio_phy_addr = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ config->qfprom_mmio_name);
+
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find qfprom resource\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ hdmi->qfprom_mmio_len = (u32)resource_size(res);
+
hdmi->qfprom_mmio = msm_ioremap(pdev,
config->qfprom_mmio_name, "HDMI_QFPROM");
+
if (IS_ERR(hdmi->qfprom_mmio)) {
- dev_info(&pdev->dev, "can't find qfprom resource\n");
+ dev_info(&pdev->dev, "can't map qfprom resource\n");
hdmi->qfprom_mmio = NULL;
}
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ config->hdcp_mmio_name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find hdcp resource: %d\n", ret);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ hdmi->hdcp_mmio_len = (u32)resource_size(res);
+ hdmi->hdcp_mmio = msm_ioremap(pdev,
+ config->hdcp_mmio_name, "HDMI_HDCP");
+ if (IS_ERR(hdmi->hdcp_mmio)) {
+ dev_info(&pdev->dev, "can't map hdcp resource\n");
+ hdmi->hdcp_mmio = NULL;
+ }
+
hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) *
config->hpd_reg_cnt, GFP_KERNEL);
if (!hdmi->hpd_regs) {
@@ -468,6 +504,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
hdmi_cfg->mmio_name = "core_physical";
hdmi_cfg->qfprom_mmio_name = "qfprom_physical";
+ hdmi_cfg->hdcp_mmio_name = "hdcp_physical";
hdmi_cfg->ddc_clk_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk");
hdmi_cfg->ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data");
hdmi_cfg->hpd_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd");
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
index 9ce8ff513210..8ca7b36ee0c8 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -27,6 +27,11 @@
#include "msm_drv.h"
#include "hdmi.xml.h"
+#define HDMI_SEC_TO_MS 1000
+#define HDMI_MS_TO_US 1000
+#define HDMI_SEC_TO_US (HDMI_SEC_TO_MS * HDMI_MS_TO_US)
+#define HDMI_KHZ_TO_HZ 1000
+#define HDMI_BUSY_WAIT_DELAY_US 100
struct hdmi_phy;
struct hdmi_platform_config;
@@ -51,9 +56,14 @@ struct hdmi {
/* video state: */
bool power_on;
unsigned long int pixclock;
+ unsigned long int actual_pixclock;
void __iomem *mmio;
void __iomem *qfprom_mmio;
+ void __iomem *hdcp_mmio;
+ u32 mmio_len;
+ u32 qfprom_mmio_len;
+ u32 hdcp_mmio_len;
phys_addr_t mmio_phy_addr;
struct regulator **hpd_regs;
@@ -72,10 +82,14 @@ struct hdmi {
bool hdmi_mode; /* are we in hdmi mode? */
bool is_hdcp_supported;
int irq;
+ void (*ddc_sw_done_cb)(void *data);
+ void *sw_done_cb_data;
struct workqueue_struct *workq;
struct hdmi_hdcp_ctrl *hdcp_ctrl;
-
+ bool use_hard_timeout;
+ int busy_wait_us;
+ u32 timeout_count;
/*
* spinlock to protect registers shared by different execution
* REG_HDMI_CTRL
@@ -91,7 +105,7 @@ struct hdmi_platform_config {
struct hdmi_phy *(*phy_init)(struct hdmi *hdmi);
const char *mmio_name;
const char *qfprom_mmio_name;
-
+ const char *hdcp_mmio_name;
/* regulators that need to be on for hpd: */
const char **hpd_reg_names;
int hpd_reg_cnt;
@@ -116,8 +130,20 @@ struct hdmi_platform_config {
int mux_lpm_gpio;
};
+struct hdmi_i2c_adapter {
+ struct i2c_adapter base;
+ struct hdmi *hdmi;
+ bool sw_done;
+ wait_queue_head_t ddc_event;
+};
+
void hdmi_set_mode(struct hdmi *hdmi, bool power_on);
+#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base)
+
+int ddc_clear_irq(struct hdmi *hdmi);
+void init_ddc(struct hdmi *hdmi);
+
static inline void hdmi_write(struct hdmi *hdmi, u32 reg, u32 data)
{
msm_writel(data, hdmi->mmio + reg);
@@ -187,6 +213,13 @@ void hdmi_i2c_destroy(struct i2c_adapter *i2c);
struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi);
/*
+ * DDC utility functions
+ */
+int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len, bool self_retry);
+int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len, bool self_retry);
+/*
* hdcp
*/
struct hdmi_hdcp_ctrl *hdmi_hdcp_ctrl_init(struct hdmi *hdmi);
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/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
index e56a8675c0a4..66be37bea4f6 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -85,84 +85,6 @@ struct hdmi_hdcp_ctrl {
bool max_dev_exceeded;
};
-static int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len)
-{
- int rc;
- int retry = 5;
- struct i2c_msg msgs[] = {
- {
- .addr = addr >> 1,
- .flags = 0,
- .len = 1,
- .buf = &offset,
- }, {
- .addr = addr >> 1,
- .flags = I2C_M_RD,
- .len = data_len,
- .buf = data,
- }
- };
-
- DBG("Start DDC read");
-retry:
- rc = i2c_transfer(hdmi->i2c, msgs, 2);
-
- retry--;
- if (rc == 2)
- rc = 0;
- else if (retry > 0)
- goto retry;
- else
- rc = -EIO;
-
- DBG("End DDC read %d", rc);
-
- return rc;
-}
-
-#define HDCP_DDC_WRITE_MAX_BYTE_NUM 32
-
-static int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len)
-{
- int rc;
- int retry = 10;
- u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM];
- struct i2c_msg msgs[] = {
- {
- .addr = addr >> 1,
- .flags = 0,
- .len = 1,
- }
- };
-
- DBG("Start DDC write");
- if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) {
- pr_err("%s: write size too big\n", __func__);
- return -ERANGE;
- }
-
- buf[0] = offset;
- memcpy(&buf[1], data, data_len);
- msgs[0].buf = buf;
- msgs[0].len = data_len + 1;
-retry:
- rc = i2c_transfer(hdmi->i2c, msgs, 1);
-
- retry--;
- if (rc == 1)
- rc = 0;
- else if (retry > 0)
- goto retry;
- else
- rc = -EIO;
-
- DBG("End DDC write %d", rc);
-
- return rc;
-}
-
static int hdmi_hdcp_scm_wr(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 *preg,
u32 *pdata, u32 count)
{
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
index f4ab7f70fed1..c65cc908b882 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
@@ -17,66 +17,16 @@
#include "hdmi.h"
-struct hdmi_i2c_adapter {
- struct i2c_adapter base;
- struct hdmi *hdmi;
- bool sw_done;
- wait_queue_head_t ddc_event;
-};
-#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base)
-
-static void init_ddc(struct hdmi_i2c_adapter *hdmi_i2c)
-{
- struct hdmi *hdmi = hdmi_i2c->hdmi;
-
- hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
- HDMI_DDC_CTRL_SW_STATUS_RESET);
- hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
- HDMI_DDC_CTRL_SOFT_RESET);
-
- hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
- HDMI_DDC_SPEED_THRESHOLD(2) |
- HDMI_DDC_SPEED_PRESCALE(10));
-
- hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
- HDMI_DDC_SETUP_TIMEOUT(0xff));
+#define MAX_TRANSACTIONS 4
- /* enable reference timer for 27us */
- hdmi_write(hdmi, REG_HDMI_DDC_REF,
- HDMI_DDC_REF_REFTIMER_ENABLE |
- HDMI_DDC_REF_REFTIMER(27));
-}
+#define SDE_DDC_TXN_CNT_MASK 0x07ff0000
+#define SDE_DDC_TXN_CNT_SHIFT 16
-static int ddc_clear_irq(struct hdmi_i2c_adapter *hdmi_i2c)
+static inline uint32_t SDE_HDMI_I2C_TRANSACTION_REG_CNT(uint32_t val)
{
- struct hdmi *hdmi = hdmi_i2c->hdmi;
- struct drm_device *dev = hdmi->dev;
- uint32_t retry = 0xffff;
- uint32_t ddc_int_ctrl;
-
- do {
- --retry;
-
- hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
- HDMI_DDC_INT_CTRL_SW_DONE_ACK |
- HDMI_DDC_INT_CTRL_SW_DONE_MASK);
-
- ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
-
- } while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry);
-
- if (!retry) {
- dev_err(dev->dev, "timeout waiting for DDC\n");
- return -ETIMEDOUT;
- }
-
- hdmi_i2c->sw_done = false;
-
- return 0;
+ return ((val) << SDE_DDC_TXN_CNT_SHIFT) & SDE_DDC_TXN_CNT_MASK;
}
-#define MAX_TRANSACTIONS 4
-
static bool sw_done(struct hdmi_i2c_adapter *hdmi_i2c)
{
struct hdmi *hdmi = hdmi_i2c->hdmi;
@@ -115,12 +65,13 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
WARN_ON(!(hdmi_read(hdmi, REG_HDMI_CTRL) & HDMI_CTRL_ENABLE));
+
if (num == 0)
return num;
- init_ddc(hdmi_i2c);
+ init_ddc(hdmi);
- ret = ddc_clear_irq(hdmi_i2c);
+ ret = ddc_clear_irq(hdmi);
if (ret)
return ret;
@@ -155,7 +106,7 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
}
}
- i2c_trans = HDMI_I2C_TRANSACTION_REG_CNT(p->len) |
+ i2c_trans = SDE_HDMI_I2C_TRANSACTION_REG_CNT(p->len) |
HDMI_I2C_TRANSACTION_REG_RW(
(p->flags & I2C_M_RD) ? DDC_READ : DDC_WRITE) |
HDMI_I2C_TRANSACTION_REG_START;
@@ -177,9 +128,13 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
ret = -ETIMEDOUT;
dev_warn(dev->dev, "DDC timeout: %d\n", ret);
DBG("sw_status=%08x, hw_status=%08x, int_ctrl=%08x",
- hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS),
- hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS),
- hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL));
+ hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS),
+ hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS),
+ hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL));
+ if (hdmi->use_hard_timeout) {
+ hdmi->use_hard_timeout = false;
+ hdmi->timeout_count = 0;
+ }
return ret;
}
@@ -213,6 +168,10 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
}
}
+ if (hdmi->use_hard_timeout) {
+ hdmi->use_hard_timeout = false;
+ hdmi->timeout_count = jiffies_to_msecs(ret);
+ }
return i;
}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_util.c b/drivers/gpu/drm/msm/hdmi/hdmi_util.c
new file mode 100644
index 000000000000..c7cfa38ed3ad
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_util.c
@@ -0,0 +1,143 @@
+/*
+ * 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 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of_irq.h>
+#include "hdmi.h"
+
+void init_ddc(struct hdmi *hdmi)
+{
+ hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
+ HDMI_DDC_CTRL_SW_STATUS_RESET);
+ hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
+ HDMI_DDC_CTRL_SOFT_RESET);
+
+ hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
+ HDMI_DDC_SPEED_THRESHOLD(2) |
+ HDMI_DDC_SPEED_PRESCALE(10));
+
+ hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
+ HDMI_DDC_SETUP_TIMEOUT(0xff));
+
+ /* enable reference timer for 19us */
+ hdmi_write(hdmi, REG_HDMI_DDC_REF,
+ HDMI_DDC_REF_REFTIMER_ENABLE |
+ HDMI_DDC_REF_REFTIMER(19));
+}
+
+int ddc_clear_irq(struct hdmi *hdmi)
+{
+ struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(hdmi->i2c);
+ struct drm_device *dev = hdmi->dev;
+ uint32_t retry = 0xffff;
+ uint32_t ddc_int_ctrl;
+
+ do {
+ --retry;
+
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
+ HDMI_DDC_INT_CTRL_SW_DONE_ACK |
+ HDMI_DDC_INT_CTRL_SW_DONE_MASK);
+
+ ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
+
+ } while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry);
+
+ if (!retry) {
+ dev_err(dev->dev, "timeout waiting for DDC\n");
+ return -ETIMEDOUT;
+ }
+
+ hdmi_i2c->sw_done = false;
+
+ return 0;
+}
+
+int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
+u8 *data, u16 data_len, bool self_retry)
+{
+ int rc;
+ int retry = 10;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = addr >> 1,
+ .flags = 0,
+ .len = 1,
+ .buf = &offset,
+ }, {
+ .addr = addr >> 1,
+ .flags = I2C_M_RD,
+ .len = data_len,
+ .buf = data,
+ }
+ };
+
+ DBG("Start DDC read");
+retry:
+ rc = i2c_transfer(hdmi->i2c, msgs, 2);
+ retry--;
+
+ if (rc == 2)
+ rc = 0;
+ else if (self_retry && (retry > 0))
+ goto retry;
+ else
+ rc = -EIO;
+
+ DBG("End DDC read %d", rc);
+
+ return rc;
+}
+
+#define HDCP_DDC_WRITE_MAX_BYTE_NUM 1024
+
+int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len, bool self_retry)
+{
+ int rc;
+ int retry = 10;
+ u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = addr >> 1,
+ .flags = 0,
+ .len = 1,
+ }
+ };
+
+ pr_debug("TESTING ! REMOVE RETRY Start DDC write");
+ if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) {
+ pr_err("%s: write size too big\n", __func__);
+ return -ERANGE;
+ }
+
+ buf[0] = offset;
+ memcpy(&buf[1], data, data_len);
+ msgs[0].buf = buf;
+ msgs[0].len = data_len + 1;
+retry:
+ rc = i2c_transfer(hdmi->i2c, msgs, 1);
+ retry--;
+ if (rc == 1)
+ rc = 0;
+ else if (self_retry && (retry > 0))
+ goto retry;
+ else
+ rc = -EIO;
+
+ DBG("End DDC write %d", rc);
+
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index caad2be87ae4..e5f42fe983c1 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -392,7 +392,7 @@ static void update_cursor(struct drm_crtc *crtc)
if (next_bo) {
/* take a obj ref + iova ref when we start scanning out: */
drm_gem_object_reference(next_bo);
- msm_gem_get_iova_locked(next_bo, mdp4_kms->aspace,
+ msm_gem_get_iova(next_bo, mdp4_kms->aspace,
&iova);
/* enable cursor: */
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
index 119221eacb43..40509434a913 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
@@ -536,9 +536,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
goto fail;
}
- mutex_lock(&dev->struct_mutex);
mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC);
- mutex_unlock(&dev->struct_mutex);
if (IS_ERR(mdp4_kms->blank_cursor_bo)) {
ret = PTR_ERR(mdp4_kms->blank_cursor_bo);
dev_err(dev->dev, "could not allocate blank-cursor bo: %d\n", ret);
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index fa746d71cd3b..0c119ec5d97c 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -19,6 +19,7 @@
#include "msm_drv.h"
#include "msm_kms.h"
#include "msm_gem.h"
+#include "sde_trace.h"
struct msm_commit {
struct drm_device *dev;
@@ -108,6 +109,7 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
struct drm_crtc_state *old_crtc_state;
int i;
+ SDE_ATRACE_BEGIN("msm_disable");
for_each_connector_in_state(old_state, connector, old_conn_state, i) {
const struct drm_encoder_helper_funcs *funcs;
struct drm_encoder *encoder;
@@ -188,6 +190,7 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
else
funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
}
+ SDE_ATRACE_END("msm_disable");
}
static void
@@ -297,6 +300,7 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
int bridge_enable_count = 0;
int i;
+ SDE_ATRACE_BEGIN("msm_enable");
for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
const struct drm_crtc_helper_funcs *funcs;
@@ -364,8 +368,10 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
}
/* If no bridges were pre_enabled, skip iterating over them again */
- if (bridge_enable_count == 0)
+ if (bridge_enable_count == 0) {
+ SDE_ATRACE_END("msm_enable");
return;
+ }
for_each_connector_in_state(old_state, connector, old_conn_state, i) {
struct drm_encoder *encoder;
@@ -385,6 +391,7 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
drm_bridge_enable(encoder->bridge);
}
+ SDE_ATRACE_END("msm_enable");
}
/* The (potentially) asynchronous part of the commit. At this point
@@ -457,7 +464,9 @@ static void _msm_drm_commit_work_cb(struct kthread_work *work)
commit = container_of(work, struct msm_commit, commit_work);
+ SDE_ATRACE_BEGIN("complete_commit");
complete_commit(commit);
+ SDE_ATRACE_END("complete_commit");
}
static struct msm_commit *commit_init(struct drm_atomic_state *state)
@@ -553,9 +562,12 @@ int msm_atomic_commit(struct drm_device *dev,
struct msm_commit *commit;
int i, ret;
+ SDE_ATRACE_BEGIN("atomic_commit");
ret = drm_atomic_helper_prepare_planes(dev, state);
- if (ret)
+ if (ret) {
+ SDE_ATRACE_END("atomic_commit");
return ret;
+ }
commit = commit_init(state);
if (IS_ERR_OR_NULL(commit)) {
@@ -635,6 +647,7 @@ int msm_atomic_commit(struct drm_device *dev,
if (async) {
msm_queue_fence_cb(dev, &commit->fence_cb, commit->fence);
+ SDE_ATRACE_END("atomic_commit");
return 0;
}
@@ -645,9 +658,11 @@ int msm_atomic_commit(struct drm_device *dev,
complete_commit(commit);
+ SDE_ATRACE_END("atomic_commit");
return 0;
error:
drm_atomic_helper_cleanup_planes(dev, state);
+ SDE_ATRACE_END("atomic_commit");
return ret;
}
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 665f8db0e04f..2280f8cb7183 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -26,11 +26,68 @@
#include "msm_gem.h"
#include "msm_mmu.h"
+static void msm_drm_helper_hotplug_event(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+ char *event_string;
+ char const *connector_name;
+ char *envp[2];
+
+ if (!dev) {
+ DRM_ERROR("hotplug_event failed, invalid input\n");
+ return;
+ }
+
+ if (!dev->mode_config.poll_enabled)
+ return;
+
+ event_string = kzalloc(SZ_4K, GFP_KERNEL);
+ if (!event_string) {
+ DRM_ERROR("failed to allocate event string\n");
+ return;
+ }
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_for_each_connector(connector, dev) {
+ /* Only handle HPD capable connectors. */
+ if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
+ continue;
+
+ connector->status = connector->funcs->detect(connector, false);
+
+ if (connector->name)
+ connector_name = connector->name;
+ else
+ connector_name = "unknown";
+
+ snprintf(event_string, SZ_4K, "name=%s status=%s\n",
+ connector_name,
+ drm_get_connector_status_name(connector->status));
+ DRM_DEBUG("generating hotplug event [%s]\n", event_string);
+ envp[0] = event_string;
+ envp[1] = NULL;
+ kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
+ envp);
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+ kfree(event_string);
+}
+
static void msm_fb_output_poll_changed(struct drm_device *dev)
{
- struct msm_drm_private *priv = dev->dev_private;
+ struct msm_drm_private *priv = NULL;
+
+ if (!dev) {
+ DRM_ERROR("output_poll_changed failed, invalid input\n");
+ return;
+ }
+
+ priv = dev->dev_private;
+
if (priv->fbdev)
drm_fb_helper_hotplug_event(priv->fbdev);
+ else
+ msm_drm_helper_hotplug_event(dev);
}
static const struct drm_mode_config_funcs mode_config_funcs = {
@@ -259,8 +316,8 @@ static int get_mdp_ver(struct platform_device *pdev)
static const struct of_device_id match_types[] = { {
.compatible = "qcom,sde-kms",
.data = (void *)KMS_SDE,
- /* end node */
- } };
+ },
+ {} };
struct device *dev = &pdev->dev;
const struct of_device_id *match;
match = of_match_node(match_types, dev->of_node);
@@ -322,6 +379,7 @@ static int msm_init_vram(struct drm_device *dev)
priv->vram.size = size;
drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1);
+ spin_lock_init(&priv->vram.lock);
dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
@@ -390,6 +448,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);
@@ -558,7 +618,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);
@@ -598,7 +659,10 @@ static int msm_open(struct drm_device *dev, struct drm_file *file)
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- INIT_LIST_HEAD(&ctx->counters);
+ if (ctx)
+ INIT_LIST_HEAD(&ctx->counters);
+
+ msm_submitqueue_init(ctx);
file->driver_priv = ctx;
@@ -628,15 +692,19 @@ 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);
- mutex_lock(&dev->struct_mutex);
- 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);
+ }
}
- mutex_unlock(&dev->struct_mutex);
kfree(ctx);
}
@@ -1142,6 +1210,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);
@@ -1194,6 +1276,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;
@@ -1204,10 +1287,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)
@@ -1224,6 +1307,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);
}
@@ -1658,6 +1749,51 @@ 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;
+ }
+
+ if (args->flags & MSM_SUBMITQUEUE_BYPASS_QOS_TIMEOUT &&
+ !capable(CAP_SYS_ADMIN)) {
+ DRM_ERROR(
+ "Only CAP_SYS_ADMIN processes can bypass the timer\n");
+ return -EPERM;
+ }
+
+ return msm_submitqueue_create(file->driver_priv, args->prio,
+ args->flags, &args->id);
+}
+
+static int msm_ioctl_submitqueue_query(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_msm_submitqueue_query *args = data;
+ void __user *ptr = (void __user *)(uintptr_t) args->data;
+
+ return msm_submitqueue_query(file->driver_priv, args->id,
+ args->param, ptr, args->len);
+}
+
+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;
@@ -1701,6 +1837,14 @@ 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),
+ DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_QUERY, msm_ioctl_submitqueue_query,
+ DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct vm_operations_struct vm_ops = {
@@ -1753,6 +1897,7 @@ static struct drm_driver msm_driver = {
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = drm_gem_prime_export,
.gem_prime_import = drm_gem_prime_import,
+ .gem_prime_res_obj = msm_gem_prime_res_obj,
.gem_prime_pin = msm_gem_prime_pin,
.gem_prime_unpin = msm_gem_prime_unpin,
.gem_prime_get_sg_table = msm_gem_prime_get_sg_table,
@@ -1952,6 +2097,7 @@ static struct platform_driver msm_platform_driver = {
.name = "msm_drm",
.of_match_table = dt_match,
.pm = &msm_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = msm_id,
};
@@ -1969,6 +2115,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();
@@ -1984,6 +2131,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 94f429cb83a7..d696f05e0459 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
@@ -137,6 +143,8 @@ enum msm_mdp_crtc_property {
enum msm_mdp_conn_property {
/* blob properties, always put these first */
CONNECTOR_PROP_SDE_INFO,
+ CONNECTOR_PROP_HDR_INFO,
+ CONNECTOR_PROP_HDR_METADATA,
/* # of blob properties */
CONNECTOR_PROP_BLOBCOUNT,
@@ -149,6 +157,7 @@ enum msm_mdp_conn_property {
CONNECTOR_PROP_DST_W,
CONNECTOR_PROP_DST_H,
CONNECTOR_PROP_PLL_DELTA,
+ CONNECTOR_PROP_PLL_ENABLE,
/* enum/bitmask properties */
CONNECTOR_PROP_TOPOLOGY_NAME,
@@ -227,6 +236,14 @@ struct msm_display_info {
};
/**
+ * struct - msm_display_kickoff_params - info for display features at kickoff
+ * @hdr_metadata: HDR metadata info passed from userspace
+ */
+struct msm_display_kickoff_params {
+ struct drm_msm_ext_panel_hdr_metadata *hdr_metadata;
+};
+
+/**
* struct msm_drm_event - defines custom event notification struct
* @base: base object required for event notification by DRM framework.
* @event: event object required for event notification by DRM framework.
@@ -325,6 +342,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];
@@ -341,6 +363,7 @@ struct msm_drm_private {
* and position mm_node->start is in # of pages:
*/
struct drm_mm mm;
+ spinlock_t lock; /* Protects drm_mm node allocation/removal */
} vram;
struct msm_vblank_ctrl vblank_ctrl;
@@ -403,10 +426,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);
@@ -423,6 +451,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);
@@ -431,8 +460,6 @@ int msm_gem_mmap_obj(struct drm_gem_object *obj,
int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
-int msm_gem_get_iova_locked(struct drm_gem_object *obj,
- struct msm_gem_address_space *aspace, uint64_t *iova);
int msm_gem_get_iova(struct drm_gem_object *obj,
struct msm_gem_address_space *aspace, uint64_t *iova);
uint64_t msm_gem_iova(struct drm_gem_object *obj,
@@ -449,11 +476,11 @@ struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj);
void *msm_gem_prime_vmap(struct drm_gem_object *obj);
void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+struct reservation_object *msm_gem_prime_res_obj(struct drm_gem_object *obj);
struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach, struct sg_table *sg);
int msm_gem_prime_pin(struct drm_gem_object *obj);
void msm_gem_prime_unpin(struct drm_gem_object *obj);
-void *msm_gem_vaddr_locked(struct drm_gem_object *obj);
void *msm_gem_vaddr(struct drm_gem_object *obj);
int msm_gem_queue_inactive_cb(struct drm_gem_object *obj,
struct msm_fence_cb *cb);
@@ -468,10 +495,23 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
uint32_t size, uint32_t flags, uint32_t *handle);
struct drm_gem_object *msm_gem_new(struct drm_device *dev,
uint32_t size, uint32_t flags);
+struct drm_gem_object *msm_gem_new_locked(struct drm_device *dev,
+ uint32_t size, uint32_t flags);
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);
+void *msm_gem_kernel_new(struct drm_device *dev, uint32_t size,
+ uint32_t flags, struct msm_gem_address_space *aspace,
+ struct drm_gem_object **bo, uint64_t *iova);
+void *msm_gem_kernel_new_locked(struct drm_device *dev, uint32_t size,
+ uint32_t flags, struct msm_gem_address_space *aspace,
+ struct drm_gem_object **bo, uint64_t *iova);
int msm_framebuffer_prepare(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace);
void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
@@ -487,6 +527,19 @@ 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_query(struct msm_file_private *ctx, u32 id, u32 param,
+ void __user *data, u32 len);
+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_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index cf127250c0d0..28b98cc1433c 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -104,10 +104,8 @@ static int msm_fbdev_create(struct drm_fb_helper *helper,
/* allocate backing bo */
size = mode_cmd.pitches[0] * mode_cmd.height;
DBG("allocating %d bytes for fb %d", size, dev->primary->index);
- mutex_lock(&dev->struct_mutex);
fbdev->bo = msm_gem_new(dev, size, MSM_BO_SCANOUT |
MSM_BO_WC | MSM_BO_STOLEN);
- mutex_unlock(&dev->struct_mutex);
if (IS_ERR(fbdev->bo)) {
ret = PTR_ERR(fbdev->bo);
fbdev->bo = NULL;
@@ -133,7 +131,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper,
* in panic (ie. lock-safe, etc) we could avoid pinning the
* buffer now:
*/
- ret = msm_gem_get_iova_locked(fbdev->bo, 0, &paddr);
+ ret = msm_gem_get_iova(fbdev->bo, 0, &paddr);
if (ret) {
dev_err(dev->dev, "failed to get buffer obj iova: %d\n", ret);
goto fail_unlock;
@@ -163,7 +161,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper,
/* FIXME: Verify paddr < 32 bits? */
dev->mode_config.fb_base = lower_32_bits(paddr);
- fbi->screen_base = msm_gem_vaddr_locked(fbdev->bo);
+ fbi->screen_base = msm_gem_vaddr(fbdev->bo);
fbi->screen_size = fbdev->bo->size;
fbi->fix.smem_start = lower_32_bits(paddr);
fbi->fix.smem_len = fbdev->bo->size;
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 4bee797da746..d66071672c62 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;
@@ -63,8 +186,7 @@ static bool use_pages(struct drm_gem_object *obj)
}
/* allocate pages from VRAM carveout, used when no IOMMU: */
-static struct page **get_pages_vram(struct drm_gem_object *obj,
- int npages)
+static struct page **get_pages_vram(struct drm_gem_object *obj, int npages)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
struct msm_drm_private *priv = obj->dev->dev_private;
@@ -76,8 +198,10 @@ static struct page **get_pages_vram(struct drm_gem_object *obj,
if (!p)
return ERR_PTR(-ENOMEM);
+ spin_lock(&priv->vram.lock);
ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node,
npages, 0, DRM_MM_SEARCH_DEFAULT);
+ spin_unlock(&priv->vram.lock);
if (ret) {
drm_free_large(p);
return ERR_PTR(ret);
@@ -92,7 +216,6 @@ static struct page **get_pages_vram(struct drm_gem_object *obj,
return p;
}
-/* called with dev->struct_mutex held */
static struct page **get_pages(struct drm_gem_object *obj)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
@@ -113,13 +236,15 @@ static struct page **get_pages(struct drm_gem_object *obj)
return p;
}
+ msm_obj->pages = p;
+
msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
if (IS_ERR(msm_obj->sgt)) {
- dev_err(dev->dev, "failed to allocate sgt\n");
- return ERR_CAST(msm_obj->sgt);
- }
+ void *ptr = ERR_CAST(msm_obj->sgt);
- msm_obj->pages = p;
+ msm_obj->sgt = NULL;
+ return ptr;
+ }
/*
* Make sure to flush the CPU cache for newly allocated memory
@@ -147,6 +272,18 @@ static struct page **get_pages(struct drm_gem_object *obj)
return msm_obj->pages;
}
+static void put_pages_vram(struct drm_gem_object *obj)
+{
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
+ struct msm_drm_private *priv = obj->dev->dev_private;
+
+ spin_lock(&priv->vram.lock);
+ drm_mm_remove_node(msm_obj->vram_node);
+ spin_unlock(&priv->vram.lock);
+
+ drm_free_large(msm_obj->pages);
+}
+
static void put_pages(struct drm_gem_object *obj)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
@@ -157,14 +294,22 @@ static void put_pages(struct drm_gem_object *obj)
msm_obj->flags &= ~MSM_BO_LOCKED;
}
- sg_free_table(msm_obj->sgt);
+ if (msm_obj->sgt)
+ sg_free_table(msm_obj->sgt);
kfree(msm_obj->sgt);
if (use_pages(obj)) {
- drm_gem_put_pages(obj, msm_obj->pages, true, false);
+ 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 {
- drm_mm_remove_node(msm_obj->vram_node);
- drm_free_large(msm_obj->pages);
+ put_pages_vram(obj);
}
msm_obj->pages = NULL;
@@ -173,11 +318,12 @@ static void put_pages(struct drm_gem_object *obj)
struct page **msm_gem_get_pages(struct drm_gem_object *obj)
{
- struct drm_device *dev = obj->dev;
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
struct page **p;
- mutex_lock(&dev->struct_mutex);
+
+ mutex_lock(&msm_obj->lock);
p = get_pages(obj);
- mutex_unlock(&dev->struct_mutex);
+ mutex_unlock(&msm_obj->lock);
return p;
}
@@ -191,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;
}
@@ -237,16 +383,17 @@ int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct drm_gem_object *obj = vma->vm_private_data;
- struct drm_device *dev = obj->dev;
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
struct page **pages;
unsigned long pfn;
pgoff_t pgoff;
int ret;
- /* Make sure we don't parallel update on a fault, nor move or remove
- * something from beneath our feet
+ /*
+ * vm_ops.open and close get and put a reference on obj.
+ * So, we dont need to hold one here.
*/
- ret = mutex_lock_interruptible(&dev->struct_mutex);
+ ret = mutex_lock_interruptible(&msm_obj->lock);
if (ret)
goto out;
@@ -269,7 +416,7 @@ int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
out_unlock:
- mutex_unlock(&dev->struct_mutex);
+ mutex_unlock(&msm_obj->lock);
out:
switch (ret) {
case -EAGAIN:
@@ -293,9 +440,10 @@ out:
static uint64_t mmap_offset(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
int ret;
- WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+ WARN_ON(!mutex_is_locked(&msm_obj->lock));
/* Make it mmapable */
ret = drm_gem_create_mmap_offset(obj);
@@ -311,9 +459,11 @@ static uint64_t mmap_offset(struct drm_gem_object *obj)
uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj)
{
uint64_t offset;
- mutex_lock(&obj->dev->struct_mutex);
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
+
+ mutex_lock(&msm_obj->lock);
offset = mmap_offset(obj);
- mutex_unlock(&obj->dev->struct_mutex);
+ mutex_unlock(&msm_obj->lock);
return offset;
}
@@ -325,19 +475,26 @@ static void obj_remove_domain(struct msm_gem_vma *domain)
}
}
+/* Called with msm_obj->lock locked */
static void
put_iova(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;
struct msm_gem_vma *domain, *tmp;
+ bool invalid = false;
- WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+ 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);
@@ -378,14 +535,8 @@ static struct msm_gem_vma *obj_get_domain(struct drm_gem_object *obj,
#define IOMMU_PRIV 0
#endif
-/* should be called under struct_mutex.. although it can be called
- * from atomic context without struct_mutex to acquire an extra
- * iova ref if you know one is already held.
- *
- * That means when I do eventually need to add support for unpinning
- * the refcnt counter needs to be atomic_t.
- */
-int msm_gem_get_iova_locked(struct drm_gem_object *obj,
+/* A reference to obj must be held before calling this function. */
+int msm_gem_get_iova(struct drm_gem_object *obj,
struct msm_gem_address_space *aspace, uint64_t *iova)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
@@ -393,13 +544,18 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj,
struct msm_gem_vma *domain;
int ret = 0;
+ mutex_lock(&msm_obj->lock);
+
if (!iommu_present(&platform_bus_type)) {
pages = get_pages(obj);
- if (IS_ERR(pages))
+ if (IS_ERR(pages)) {
+ mutex_unlock(&msm_obj->lock);
return PTR_ERR(pages);
+ }
*iova = (uint64_t) physaddr(obj);
+ mutex_unlock(&msm_obj->lock);
return 0;
}
@@ -407,12 +563,15 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj,
if (!domain) {
domain = obj_add_domain(obj, aspace);
- if (IS_ERR(domain))
+ if (IS_ERR(domain)) {
+ mutex_unlock(&msm_obj->lock);
return PTR_ERR(domain);
+ }
pages = get_pages(obj);
if (IS_ERR(pages)) {
obj_remove_domain(domain);
+ mutex_unlock(&msm_obj->lock);
return PTR_ERR(pages);
}
@@ -425,26 +584,8 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj,
else
obj_remove_domain(domain);
- return ret;
-}
-
-/* get iova, taking a reference. Should have a matching put */
-int msm_gem_get_iova(struct drm_gem_object *obj,
- struct msm_gem_address_space *aspace, uint64_t *iova)
-{
- struct msm_gem_vma *domain;
- int ret;
-
- domain = obj_get_domain(obj, aspace);
- if (domain) {
- *iova = domain->iova;
- return 0;
- }
-
- mutex_lock(&obj->dev->struct_mutex);
- ret = msm_gem_get_iova_locked(obj, aspace, iova);
- mutex_unlock(&obj->dev->struct_mutex);
- return ret;
+ mutex_unlock(&msm_obj->lock);
+ return 0;
}
/* get iova without taking a reference, used in places where you have
@@ -453,11 +594,17 @@ int msm_gem_get_iova(struct drm_gem_object *obj,
uint64_t msm_gem_iova(struct drm_gem_object *obj,
struct msm_gem_address_space *aspace)
{
- struct msm_gem_vma *domain = obj_get_domain(obj, aspace);
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
+ struct msm_gem_vma *domain;
+ uint64_t iova;
+ mutex_lock(&msm_obj->lock);
+ domain = obj_get_domain(obj, aspace);
WARN_ON(!domain);
+ iova = domain ? domain->iova : 0;
+ mutex_unlock(&msm_obj->lock);
- return domain ? domain->iova : 0;
+ return iova;
}
void msm_gem_put_iova(struct drm_gem_object *obj,
@@ -501,27 +648,31 @@ fail:
return ret;
}
-void *msm_gem_vaddr_locked(struct drm_gem_object *obj)
+void *msm_gem_vaddr(struct drm_gem_object *obj)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
- WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
- if (!msm_obj->vaddr) {
+
+ mutex_lock(&msm_obj->lock);
+
+ 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))
+ if (IS_ERR(pages)) {
+ mutex_unlock(&msm_obj->lock);
return ERR_CAST(pages);
+ }
msm_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT,
VM_MAP, pgprot_writecombine(PAGE_KERNEL));
}
- return msm_obj->vaddr;
-}
+ mutex_unlock(&msm_obj->lock);
-void *msm_gem_vaddr(struct drm_gem_object *obj)
-{
- void *ret;
- mutex_lock(&obj->dev->struct_mutex);
- ret = msm_gem_vaddr_locked(obj);
- mutex_unlock(&obj->dev->struct_mutex);
- return ret;
+ return msm_obj->vaddr;
}
/* setup callback for when bo is no longer busy..
@@ -654,21 +805,32 @@ 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);
+ /* 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) {
if (msm_obj->vaddr)
dma_buf_vunmap(obj->import_attach->dmabuf,
msm_obj->vaddr);
-
/* Don't drop the pages for imported dmabuf, as they are not
* ours, just free the array we allocated:
*/
@@ -685,8 +847,12 @@ void msm_gem_free_object(struct drm_gem_object *obj)
reservation_object_fini(msm_obj->resv);
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 */
@@ -696,13 +862,28 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
struct drm_gem_object *obj;
int ret;
- ret = mutex_lock_interruptible(&dev->struct_mutex);
- if (ret)
- return ret;
-
obj = msm_gem_new(dev, size, flags);
- mutex_unlock(&dev->struct_mutex);
+ 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;
+}
+
+/* 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 drm_gem_object *obj;
+ int ret;
+
+ obj = msm_gem_svm_new(dev, file, hostptr, size, flags);
if (IS_ERR(obj))
return PTR_ERR(obj);
@@ -715,12 +896,11 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
return ret;
}
-static int msm_gem_new_impl(struct drm_device *dev,
+static int msm_gem_obj_init(struct drm_device *dev,
uint32_t size, uint32_t flags,
- struct drm_gem_object **obj)
+ 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) {
@@ -742,9 +922,7 @@ 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) {
struct msm_gem_vma *domain = obj_add_domain(&msm_obj->base, 0);
@@ -758,23 +936,45 @@ 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);
- list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
-
- *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;
}
-struct drm_gem_object *msm_gem_new(struct drm_device *dev,
- uint32_t size, uint32_t flags)
+static struct drm_gem_object *msm_gem_new_impl(struct drm_device *dev,
+ uint32_t size, uint32_t flags, bool struct_mutex_locked)
{
- struct drm_gem_object *obj = NULL;
+ struct msm_gem_object *msm_obj;
int ret;
- WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+ 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;
+ int ret;
size = PAGE_ALIGN(size);
@@ -785,9 +985,9 @@ 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);
- 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);
@@ -800,8 +1000,160 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev,
return obj;
fail:
- if (obj)
- drm_gem_object_unreference(obj);
+ drm_gem_object_unreference_unlocked(obj);
+
+ return ERR_PTR(ret);
+}
+
+struct drm_gem_object *msm_gem_new_locked(struct drm_device *dev,
+ uint32_t size, uint32_t flags)
+{
+ return _msm_gem_new(dev, size, flags, true);
+}
+
+struct drm_gem_object *msm_gem_new(struct drm_device *dev,
+ uint32_t size, uint32_t flags)
+{
+ 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;
+ 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 (!ctx)
+ return ERR_PTR(-ENODEV);
+
+ /* 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);
+ aspace = ctx->aspace;
+ 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);
}
@@ -821,21 +1173,20 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
size = PAGE_ALIGN(size);
- mutex_lock(&dev->struct_mutex);
- ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj);
- mutex_unlock(&dev->struct_mutex);
-
- 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);
npages = size / PAGE_SIZE;
msm_obj = to_msm_bo(obj);
+ mutex_lock(&msm_obj->lock);
msm_obj->sgt = sgt;
msm_obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
if (!msm_obj->pages) {
+ mutex_unlock(&msm_obj->lock);
ret = -ENOMEM;
goto fail;
}
@@ -843,15 +1194,146 @@ 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);
- if (ret)
+ ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages,
+ NULL, npages);
+ if (ret) {
+ mutex_unlock(&msm_obj->lock);
goto fail;
+ }
+
+ mutex_unlock(&msm_obj->lock);
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);
+}
+
+/*
+ * Helper function to consolidate in-kernel buffer allocations that usually need
+ * to allocate a buffer object, iova and a virtual address all in one shot
+ */
+static void *_msm_gem_kernel_new(struct drm_device *dev, uint32_t size,
+ uint32_t flags, struct msm_gem_address_space *aspace,
+ struct drm_gem_object **bo, uint64_t *iova, bool locked)
+{
+ void *vaddr;
+ struct drm_gem_object *obj = _msm_gem_new(dev, size, flags, locked);
+ int ret;
+
+ if (IS_ERR(obj))
+ return ERR_CAST(obj);
+
+ ret = msm_gem_get_iova(obj, aspace, iova);
+ if (ret) {
+ drm_gem_object_unreference(obj);
+ return ERR_PTR(ret);
+ }
+
+ vaddr = msm_gem_vaddr(obj);
+ if (!vaddr) {
+ msm_gem_put_iova(obj, aspace);
+ drm_gem_object_unreference(obj);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ *bo = obj;
+ return vaddr;
+}
+
+void *msm_gem_kernel_new(struct drm_device *dev, uint32_t size,
+ uint32_t flags, struct msm_gem_address_space *aspace,
+ struct drm_gem_object **bo, uint64_t *iova)
+{
+ return _msm_gem_kernel_new(dev, size, flags, aspace, bo, iova,
+ false);
+}
+
+void *msm_gem_kernel_new_locked(struct drm_device *dev, uint32_t size,
+ uint32_t flags, struct msm_gem_address_space *aspace,
+ struct drm_gem_object **bo, uint64_t *iova)
+{
+ return _msm_gem_kernel_new(dev, size, flags, aspace, bo, iova,
+ true);
+}
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index 41b4b5a4fd66..df9ddadc5c5c 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -20,17 +20,21 @@
#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;
struct msm_mmu *mmu;
struct kref kref;
struct drm_mm mm;
+ spinlock_t lock; /* Protects drm_mm node allocation/removal */
u64 va_len;
};
@@ -80,9 +84,36 @@ struct msm_gem_object {
* an IOMMU. Also used for stolen/splashscreen buffer.
*/
struct drm_mm_node *vram_node;
+ struct mutex lock; /* Protects resources associated with bo */
};
#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;
@@ -101,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
@@ -109,15 +143,18 @@ 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;
+ struct drm_msm_gem_submit_profile_buffer *profile_buf;
bool secure;
+ struct msm_gpu_submitqueue *queue;
+ int tick_index;
unsigned int nr_cmds;
unsigned int nr_bos;
struct {
diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c
index 678018804f3a..9f3c097d011b 100644
--- a/drivers/gpu/drm/msm/msm_gem_prime.c
+++ b/drivers/gpu/drm/msm/msm_gem_prime.c
@@ -80,3 +80,10 @@ void msm_gem_prime_unpin(struct drm_gem_object *obj)
if (!obj->import_attach)
msm_gem_put_pages(obj);
}
+
+struct reservation_object *msm_gem_prime_res_obj(struct drm_gem_object *obj)
+{
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
+
+ return msm_obj->resv;
+}
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 6d88eb374f69..b73379aa9ed7 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -18,6 +18,7 @@
#include "msm_drv.h"
#include "msm_gpu.h"
#include "msm_gem.h"
+#include "msm_trace.h"
/*
* Cmdstream submission:
@@ -35,7 +36,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,17 +50,23 @@ 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;
submit->nr_cmds = 0;
- submit->profile_buf_vaddr = NULL;
+ submit->profile_buf = NULL;
submit->profile_buf_iova = 0;
submit->cmd = (void *)&submit->bos[nr_bos];
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 +82,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,9 +235,21 @@ 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_locked(&msm_obj->base, aspace, &iova);
+ ret = msm_gem_get_iova(&msm_obj->base, aspace, &iova);
/* this would break the logic in the fail path.. there is no
* reason for this to happen, but just to be on the safe side
@@ -307,7 +337,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob
/* For now, just map the entire thing. Eventually we probably
* to do it page-by-page, w/ kmap() if not vmap()d..
*/
- ptr = msm_gem_vaddr_locked(&obj->base);
+ ptr = msm_gem_vaddr(&obj->base);
if (IS_ERR(ptr)) {
ret = PTR_ERR(ptr);
@@ -369,6 +399,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 +419,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;
@@ -397,12 +431,17 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
return -EINVAL;
gpu = priv->gpu;
- if (!gpu)
+ if (!gpu || !ctx)
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 +461,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 +494,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;
}
@@ -469,8 +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_locked(&msm_obj->base);
+ submit->profile_buf = 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 47f7436854fb..f399d24019e4 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -52,6 +52,7 @@ msm_gem_address_space_new(struct msm_mmu *mmu, const char *name,
if (!aspace)
return ERR_PTR(-ENOMEM);
+ spin_lock_init(&aspace->lock);
aspace->name = name;
aspace->mmu = mmu;
@@ -77,14 +78,19 @@ static int allocate_iova(struct msm_gem_address_space *aspace,
if (!aspace->va_len)
return 0;
- if (WARN_ON(drm_mm_node_allocated(&vma->node)))
- return 0;
-
for_each_sg(sgt->sgl, sg, sgt->nents, i)
size += sg->length + sg->offset;
- ret = drm_mm_insert_node(&aspace->mm, &vma->node, size >> PAGE_SHIFT,
- 0, DRM_MM_SEARCH_DEFAULT);
+ spin_lock(&aspace->lock);
+
+ if (WARN_ON(drm_mm_node_allocated(&vma->node))) {
+ spin_unlock(&aspace->lock);
+ return 0;
+ }
+ ret = drm_mm_insert_node(&aspace->mm, &vma->node,
+ size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_BOTTOM_UP);
+
+ spin_unlock(&aspace->lock);
if (!ret && iova)
*iova = vma->node.start << PAGE_SHIFT;
@@ -92,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)
@@ -110,9 +155,7 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace,
flags, priv);
if (ret) {
- if (drm_mm_node_allocated(&vma->node))
- drm_mm_remove_node(&vma->node);
-
+ msm_gem_release_iova(aspace, vma);
return ret;
}
@@ -123,15 +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);
- if (drm_mm_node_allocated(&vma->node))
- drm_mm_remove_node(&vma->node);
+ 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 bef6e81de82a..6bac1cf6f7c5 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -18,7 +18,7 @@
#include "msm_gpu.h"
#include "msm_gem.h"
#include "msm_mmu.h"
-
+#include "msm_trace.h"
/*
* Power Management:
@@ -274,19 +274,32 @@ static void inactive_start(struct msm_gpu *gpu)
round_jiffies_up(jiffies + DRM_MSM_INACTIVE_JIFFIES));
}
+static void retire_guilty_submit(struct msm_gpu *gpu,
+ struct msm_ringbuffer *ring)
+{
+ struct msm_gem_submit *submit = list_first_entry_or_null(&ring->submits,
+ struct msm_gem_submit, node);
+
+ if (!submit)
+ return;
+
+ submit->queue->faults++;
+
+ msm_gem_submit_free(submit);
+}
+
/*
* 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)
{
struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work);
struct drm_device *dev = gpu->dev;
- dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
-
mutex_lock(&dev->struct_mutex);
if (msm_gpu_active(gpu)) {
struct msm_gem_submit *submit;
@@ -295,30 +308,22 @@ static void recover_worker(struct work_struct *work)
inactive_cancel(gpu);
- FOR_EACH_RING(gpu, ring, i) {
- uint32_t fence;
+ /* Retire all events that have already passed */
+ FOR_EACH_RING(gpu, ring, i)
+ retire_submits(gpu, ring,
+ 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_guilty_submit(gpu, gpu->funcs->active_ring(gpu));
/* 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);
@@ -346,8 +351,22 @@ static void hangcheck_handler(unsigned long data)
/* some progress has been made.. ya! */
gpu->hangcheck_fence[ring->id] = fence;
} else if (fence < submitted) {
- /* no progress and not done.. hung! */
+ struct msm_gem_submit *submit;
+
gpu->hangcheck_fence[ring->id] = fence;
+
+ /*
+ * No progress done, but see if the current submit is
+ * intentionally skipping the hangcheck
+ */
+ submit = list_first_entry_or_null(&ring->submits,
+ struct msm_gem_submit, node);
+
+ if (!submit || (submit->queue->flags &
+ MSM_SUBMITQUEUE_BYPASS_QOS_TIMEOUT))
+ goto out;
+
+ /* no progress and not done and not special .. hung! */
dev_err(dev->dev, "%s: hangcheck detected gpu lockup rb %d!\n",
gpu->name, ring->id);
dev_err(dev->dev, "%s: completed fence: %u\n",
@@ -358,6 +377,7 @@ static void hangcheck_handler(unsigned long data)
queue_work(priv->wq, &gpu->recover_work);
}
+out:
/* if still more pending work, reset the hangcheck timer: */
if (submitted > gpu->hangcheck_fence[ring->id])
hangcheck_timer_reset(gpu);
@@ -465,23 +485,28 @@ 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) {
+ struct msm_memptr_ticks *ticks;
- list_for_each_entry_safe(submit, tmp, &gpu->submit_list, node) {
- if (COMPARE_FENCE_LTE(submit->fence, fence)) {
- list_del(&submit->node);
- kfree(submit);
- }
+ if (submit->fence > fence)
+ break;
+
+ ticks = &(ring->memptrs->ticks[submit->tick_index]);
+
+ /* Add memory barrier to ensure the timer ticks are posted */
+ rmb();
+
+ trace_msm_retired(submit, ticks->started, ticks->retired);
+
+ msm_gem_submit_free(submit);
}
}
@@ -493,11 +518,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 +551,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,12 +581,18 @@ 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);
ring->submitted_fence = submit->fence;
+ submit->tick_index = ring->tick_index;
+ ring->tick_index = (ring->tick_index + 1) %
+ ARRAY_SIZE(ring->memptrs->ticks);
+
+ trace_msm_queued(submit);
+
update_sw_cntrs(gpu);
for (i = 0; i < submit->nr_bos; i++) {
@@ -580,8 +612,7 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
/* ring takes a reference to the bo and iova: */
drm_gem_object_reference(&msm_obj->base);
- msm_gem_get_iova_locked(&msm_obj->base,
- aspace, &iova);
+ msm_gem_get_iova(&msm_obj->base, aspace, &iova);
}
if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
@@ -653,6 +684,9 @@ int msm_gpu_counter_put(struct msm_gpu *gpu, struct drm_msm_counter *data,
{
struct msm_context_counter *entry;
+ if (!gpu || !ctx)
+ return -ENODEV;
+
list_for_each_entry(entry, &ctx->counters, node) {
if (entry->groupid == data->groupid &&
entry->counterid == data->counterid) {
@@ -794,17 +828,39 @@ msm_gpu_create_address_space(struct msm_gpu *gpu, struct device *dev,
gpu->name, name, PTR_ERR(aspace));
iommu_domain_free(iommu);
- aspace = NULL;
+ return NULL;
+ }
+
+ if (aspace->mmu) {
+ int ret = aspace->mmu->funcs->attach(aspace->mmu, NULL, 0);
+
+ if (ret) {
+ dev_err(gpu->dev->dev,
+ "%s: failed to atach IOMMU '%s': %d\n",
+ gpu->name, name, ret);
+ msm_gem_address_space_put(aspace);
+ aspace = ERR_PTR(ret);
+ }
}
return aspace;
}
+static void msm_gpu_destroy_address_space(struct msm_gem_address_space *aspace)
+{
+ if (!IS_ERR_OR_NULL(aspace) && aspace->mmu)
+ aspace->mmu->funcs->detach(aspace->mmu);
+
+ msm_gem_address_space_put(aspace);
+}
+
int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
const char *name, struct msm_gpu_config *config)
{
int i, ret, nr_rings;
+ void *memptrs;
+ uint64_t memptrs_iova;
if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs)))
gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs);
@@ -819,7 +875,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);
@@ -888,12 +943,18 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
nr_rings = ARRAY_SIZE(gpu->rb);
}
+ /* Allocate one buffer to hold all the memptr records for the rings */
+ memptrs = msm_gem_kernel_new(drm, sizeof(struct msm_memptrs) * nr_rings,
+ MSM_BO_UNCACHED, gpu->aspace, &gpu->memptrs_bo, &memptrs_iova);
+
+ if (IS_ERR(memptrs)) {
+ ret = PTR_ERR(memptrs);
+ goto fail;
+ }
+
/* Create ringbuffer(s): */
for (i = 0; i < nr_rings; i++) {
- mutex_lock(&drm->struct_mutex);
- gpu->rb[i] = msm_ringbuffer_new(gpu, i);
- mutex_unlock(&drm->struct_mutex);
-
+ gpu->rb[i] = msm_ringbuffer_new(gpu, i, memptrs, memptrs_iova);
if (IS_ERR(gpu->rb[i])) {
ret = PTR_ERR(gpu->rb[i]);
gpu->rb[i] = NULL;
@@ -901,6 +962,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
"could not create ringbuffer %d: %d\n", i, ret);
goto fail;
}
+
+ memptrs += sizeof(struct msm_memptrs);
+ memptrs_iova += sizeof(struct msm_memptrs);
}
gpu->nr_rings = nr_rings;
@@ -922,11 +986,17 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
return 0;
fail:
- for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) {
- if (gpu->rb[i])
- msm_ringbuffer_destroy(gpu->rb[i]);
+ for (i = 0; i < ARRAY_SIZE(gpu->rb); i++)
+ msm_ringbuffer_destroy(gpu->rb[i]);
+
+ if (gpu->memptrs_bo) {
+ msm_gem_put_iova(gpu->memptrs_bo, gpu->aspace);
+ drm_gem_object_unreference_unlocked(gpu->memptrs_bo);
}
+ msm_gpu_destroy_address_space(gpu->aspace);
+ msm_gpu_destroy_address_space(gpu->secure_aspace);
+
pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -944,16 +1014,17 @@ void msm_gpu_cleanup(struct msm_gpu *gpu)
bs_fini(gpu);
- for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) {
- if (!gpu->rb[i])
- continue;
-
- if (gpu->rb[i]->iova)
- msm_gem_put_iova(gpu->rb[i]->bo, gpu->aspace);
-
+ for (i = 0; i < ARRAY_SIZE(gpu->rb); i++)
msm_ringbuffer_destroy(gpu->rb[i]);
+
+ if (gpu->memptrs_bo) {
+ msm_gem_put_iova(gpu->memptrs_bo, gpu->aspace);
+ drm_gem_object_unreference_unlocked(gpu->memptrs_bo);
}
msm_snapshot_destroy(gpu, gpu->snapshot);
pm_runtime_disable(&pdev->dev);
+
+ msm_gpu_destroy_address_space(gpu->aspace);
+ msm_gpu_destroy_address_space(gpu->secure_aspace);
}
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index a47eae68dd9b..306139bcd103 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -131,6 +131,8 @@ struct msm_gpu {
struct pm_qos_request pm_qos_req_dma;
+ struct drm_gem_object *memptrs_bo;
+
#ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING
struct msm_bus_scale_pdata *bus_scale_table;
uint32_t bsc;
@@ -147,12 +149,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 +288,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 6dbb516cd4dc..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_locked(&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 14a16c4578d9..2a5843e6f81b 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -18,7 +18,8 @@
#include "msm_ringbuffer.h"
#include "msm_gpu.h"
-struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id)
+struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,
+ struct msm_memptrs *memptrs, uint64_t memptrs_iova)
{
struct msm_ringbuffer *ring;
int ret;
@@ -34,18 +35,24 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id)
ring->gpu = gpu;
ring->id = id;
- ring->bo = msm_gem_new(gpu->dev, MSM_GPU_RINGBUFFER_SZ, MSM_BO_WC);
+ ring->bo = msm_gem_new(gpu->dev, MSM_GPU_RINGBUFFER_SZ,
+ MSM_BO_WC);
if (IS_ERR(ring->bo)) {
ret = PTR_ERR(ring->bo);
ring->bo = NULL;
goto fail;
}
- ring->start = msm_gem_vaddr_locked(ring->bo);
+ ring->memptrs = memptrs;
+ ring->memptrs_iova = memptrs_iova;
+
+
+ ring->start = msm_gem_vaddr(ring->bo);
ring->end = ring->start + (MSM_GPU_RINGBUFFER_SZ >> 2);
ring->next = ring->start;
ring->cur = ring->start;
+ INIT_LIST_HEAD(&ring->submits);
spin_lock_init(&ring->lock);
return ring;
@@ -58,7 +65,10 @@ fail:
void msm_ringbuffer_destroy(struct msm_ringbuffer *ring)
{
- if (ring->bo)
+ if (ring && ring->bo) {
+ msm_gem_put_iova(ring->bo, ring->gpu->aspace);
drm_gem_object_unreference_unlocked(ring->bo);
+ }
+
kfree(ring);
}
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h
index 1e84905073bf..b19ce75a4cc9 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.h
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.h
@@ -20,6 +20,27 @@
#include "msm_drv.h"
+#define rbmemptr(ring, member) \
+ ((ring)->memptrs_iova + offsetof(struct msm_memptrs, member))
+
+struct msm_memptr_ticks {
+ uint64_t started;
+ uint64_t retired;
+};
+
+struct msm_memptrs {
+ volatile uint32_t rptr;
+ volatile uint32_t fence;
+ volatile uint64_t ttbr0;
+ volatile unsigned int contextidr;
+ struct msm_memptr_ticks ticks[128];
+};
+
+#define RING_TICKS_IOVA(ring, index, field) \
+ ((ring)->memptrs_iova + offsetof(struct msm_memptrs, ticks) + \
+ ((index) * sizeof(struct msm_memptr_ticks)) + \
+ offsetof(struct msm_memptr_ticks, field))
+
struct msm_ringbuffer {
struct msm_gpu *gpu;
int id;
@@ -28,9 +49,15 @@ struct msm_ringbuffer {
uint64_t iova;
uint32_t submitted_fence;
spinlock_t lock;
+ struct list_head submits;
+
+ struct msm_memptrs *memptrs;
+ uint64_t memptrs_iova;
+ int tick_index;
};
-struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id);
+struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,
+ struct msm_memptrs *memptrs, uint64_t memptrs_iova);
void msm_ringbuffer_destroy(struct msm_ringbuffer *ring);
/* ringbuffer helpers (the parts that are same for a3xx/a2xx/z180..) */
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index c2dd5f96521e..eb68eb977aa7 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -120,16 +120,30 @@ static int msm_smmu_map(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;
int ret;
- if (priv)
- ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents,
- DMA_BIDIRECTIONAL, priv);
- else
- ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents,
- DMA_BIDIRECTIONAL);
+ if (!client || !sgt)
+ return -EINVAL;
+
+ if (iova != 0) {
+ if (!client->mmu_mapping || !client->mmu_mapping->domain)
+ return -EINVAL;
+
+ domain = client->mmu_mapping->domain;
- return (ret != sgt->nents) ? -ENOMEM : 0;
+ return iommu_map_sg(domain, iova, sgt->sgl,
+ sgt->nents, flags);
+ } else {
+ if (priv)
+ ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl,
+ sgt->nents, DMA_BIDIRECTIONAL, priv);
+ else
+ ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL);
+
+ return (ret != sgt->nents) ? -ENOMEM : 0;
+ }
}
static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova,
@@ -173,8 +187,8 @@ static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = {
},
[MSM_SMMU_DOMAIN_SECURE] = {
.label = "mdp_s",
- .va_start = 0,
- .va_size = SZ_4G,
+ .va_start = SZ_128K,
+ .va_size = SZ_4G - SZ_128K,
.secure = true,
},
[MSM_SMMU_DOMAIN_NRT_UNSECURE] = {
@@ -185,20 +199,20 @@ static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = {
},
[MSM_SMMU_DOMAIN_NRT_SECURE] = {
.label = "rot_s",
- .va_start = 0,
- .va_size = SZ_4G,
+ .va_start = SZ_128K,
+ .va_size = SZ_4G - SZ_128K,
.secure = true,
},
};
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] },
{}
};
@@ -394,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;
@@ -404,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..4f2af876db49
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_submitqueue.c
@@ -0,0 +1,151 @@
+/* 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;
+
+ if (!ctx)
+ return NULL;
+
+ 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_query(struct msm_file_private *ctx, u32 id, u32 param,
+ void __user *data, u32 len)
+{
+ struct msm_gpu_submitqueue *queue = msm_submitqueue_get(ctx, id);
+ int ret = 0;
+
+ if (!queue)
+ return -ENOENT;
+
+ if (param == MSM_SUBMITQUEUE_PARAM_FAULTS) {
+ u32 size = min_t(u32, len, sizeof(queue->faults));
+
+ if (copy_to_user(data, &queue->faults, size))
+ ret = -EFAULT;
+ } else {
+ ret = -EINVAL;
+ }
+
+ msm_submitqueue_put(queue);
+
+ return ret;
+}
+
+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/msm_trace.h b/drivers/gpu/drm/msm/msm_trace.h
new file mode 100644
index 000000000000..68c7ff78ffc2
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_trace.h
@@ -0,0 +1,98 @@
+/* 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.
+ *
+ */
+
+#if !defined(_MSM_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _MSM_TRACE_H_
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM msm_drm
+#define TRACE_INCLUDE_FILE msm_trace
+
+TRACE_EVENT(msm_queued,
+ TP_PROTO(struct msm_gem_submit *submit),
+ TP_ARGS(submit),
+ TP_STRUCT__entry(
+ __field(uint32_t, queue_id)
+ __field(uint32_t, fence_id)
+ __field(int, ring)
+ ),
+ TP_fast_assign(
+ __entry->queue_id = submit->queue->id;
+ __entry->fence_id = submit->fence;
+ __entry->ring = submit->ring;
+ ),
+ TP_printk(
+ "queue=%u fence=%u ring=%d",
+ __entry->queue_id, __entry->fence_id, __entry->ring
+ )
+);
+
+TRACE_EVENT(msm_submitted,
+ TP_PROTO(struct msm_gem_submit *submit, uint64_t ticks, uint64_t nsecs),
+ TP_ARGS(submit, ticks, nsecs),
+ TP_STRUCT__entry(
+ __field(uint32_t, queue_id)
+ __field(uint32_t, fence_id)
+ __field(int, ring)
+ __field(uint64_t, ticks)
+ __field(uint64_t, nsecs)
+ ),
+ TP_fast_assign(
+ __entry->queue_id = submit->queue->id;
+ __entry->fence_id = submit->fence;
+ __entry->ring = submit->ring;
+ __entry->ticks = ticks;
+ __entry->nsecs = nsecs;
+ ),
+ TP_printk(
+ "queue=%u fence=%u ring=%d ticks=%lld nsecs=%llu",
+ __entry->queue_id, __entry->fence_id, __entry->ring,
+ __entry->ticks, __entry->nsecs
+ )
+);
+
+TRACE_EVENT(msm_retired,
+ TP_PROTO(struct msm_gem_submit *submit, uint64_t start_ticks,
+ uint64_t retire_ticks),
+ TP_ARGS(submit, start_ticks, retire_ticks),
+ TP_STRUCT__entry(
+ __field(uint32_t, queue_id)
+ __field(uint32_t, fence_id)
+ __field(int, ring)
+ __field(uint64_t, start_ticks)
+ __field(uint64_t, retire_ticks)
+ ),
+ TP_fast_assign(
+ __entry->queue_id = submit->queue->id;
+ __entry->fence_id = submit->fence;
+ __entry->ring = submit->ring;
+ __entry->start_ticks = start_ticks;
+ __entry->retire_ticks = retire_ticks;
+ ),
+ TP_printk(
+ "queue=%u fence=%u ring=%d started=%lld retired=%lld",
+ __entry->queue_id, __entry->fence_id, __entry->ring,
+ __entry->start_ticks, __entry->retire_ticks
+ )
+);
+
+
+#endif
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
+
diff --git a/drivers/gpu/drm/msm/msm_trace_points.c b/drivers/gpu/drm/msm/msm_trace_points.c
new file mode 100644
index 000000000000..41d9a975ac92
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_trace_points.c
@@ -0,0 +1,18 @@
+/* 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 "msm_gem.h"
+#include "msm_gpu.h"
+
+#define CREATE_TRACE_POINTS
+#include "msm_trace.h"
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 7538927a4993..0bb8298c1013 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -17,6 +17,12 @@
#include "sde_connector.h"
#include "sde_backlight.h"
+#define SDE_DEBUG_CONN(c, fmt, ...) SDE_DEBUG("conn%d " fmt,\
+ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__)
+
+#define SDE_ERROR_CONN(c, fmt, ...) SDE_ERROR("conn%d " fmt,\
+ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__)
+
static const struct drm_prop_enum_list e_topology_name[] = {
{SDE_RM_TOPOLOGY_UNKNOWN, "sde_unknown"},
{SDE_RM_TOPOLOGY_SINGLEPIPE, "sde_singlepipe"},
@@ -54,6 +60,36 @@ int sde_connector_get_info(struct drm_connector *connector,
return c_conn->ops.get_info(info, c_conn->display);
}
+int sde_connector_pre_kickoff(struct drm_connector *connector)
+{
+ struct sde_connector *c_conn;
+ struct sde_connector_state *c_state;
+ struct msm_display_kickoff_params params;
+ int rc;
+
+ if (!connector) {
+ SDE_ERROR("invalid argument\n");
+ return -EINVAL;
+ }
+
+ c_conn = to_sde_connector(connector);
+ c_state = to_sde_connector_state(connector->state);
+
+ if (!c_conn->display) {
+ SDE_ERROR("invalid argument\n");
+ return -EINVAL;
+ }
+
+ if (!c_conn->ops.pre_kickoff)
+ return 0;
+
+ params.hdr_metadata = &c_state->hdr_meta;
+
+ rc = c_conn->ops.pre_kickoff(connector, c_conn->display, &params);
+
+ return rc;
+}
+
static void sde_connector_destroy(struct drm_connector *connector)
{
struct sde_connector *c_conn;
@@ -70,7 +106,8 @@ static void sde_connector_destroy(struct drm_connector *connector)
if (c_conn->blob_caps)
drm_property_unreference_blob(c_conn->blob_caps);
-
+ if (c_conn->blob_hdr)
+ drm_property_unreference_blob(c_conn->blob_hdr);
msm_property_destroy(&c_conn->property_info);
drm_connector_unregister(connector);
@@ -204,6 +241,68 @@ sde_connector_atomic_duplicate_state(struct drm_connector *connector)
return &c_state->base;
}
+static int _sde_connector_set_hdr_info(
+ struct sde_connector *c_conn,
+ struct sde_connector_state *c_state,
+ void *usr_ptr)
+{
+ struct drm_connector *connector;
+ struct drm_msm_ext_panel_hdr_metadata *hdr_meta;
+ int i;
+
+ if (!c_conn || !c_state) {
+ SDE_ERROR_CONN(c_conn, "invalid args\n");
+ return -EINVAL;
+ }
+
+ connector = &c_conn->base;
+
+ if (!connector->hdr_supported) {
+ SDE_ERROR_CONN(c_conn, "sink doesn't support HDR\n");
+ return -ENOTSUPP;
+ }
+
+ memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
+
+ if (!usr_ptr) {
+ SDE_DEBUG_CONN(c_conn, "hdr metadata cleared\n");
+ return 0;
+ }
+
+ if (copy_from_user(&c_state->hdr_meta,
+ (void __user *)usr_ptr,
+ sizeof(*hdr_meta))) {
+ SDE_ERROR_CONN(c_conn, "failed to copy hdr metadata\n");
+ return -EFAULT;
+ }
+
+ hdr_meta = &c_state->hdr_meta;
+
+ SDE_DEBUG_CONN(c_conn, "hdr_supported %d\n",
+ hdr_meta->hdr_supported);
+ SDE_DEBUG_CONN(c_conn, "eotf %d\n",
+ hdr_meta->eotf);
+ SDE_DEBUG_CONN(c_conn, "white_point_x %d\n",
+ hdr_meta->white_point_x);
+ SDE_DEBUG_CONN(c_conn, "white_point_y %d\n",
+ hdr_meta->white_point_y);
+ SDE_DEBUG_CONN(c_conn, "max_luminance %d\n",
+ hdr_meta->max_luminance);
+ SDE_DEBUG_CONN(c_conn, "max_content_light_level %d\n",
+ hdr_meta->max_content_light_level);
+ SDE_DEBUG_CONN(c_conn, "max_average_light_level %d\n",
+ hdr_meta->max_average_light_level);
+
+ for (i = 0; i < HDR_PRIMARIES_COUNT; i++) {
+ SDE_DEBUG_CONN(c_conn, "display_primaries_x [%d]\n",
+ hdr_meta->display_primaries_x[i]);
+ SDE_DEBUG_CONN(c_conn, "display_primaries_y [%d]\n",
+ hdr_meta->display_primaries_y[i]);
+ }
+
+ return 0;
+}
+
static int sde_connector_atomic_set_property(struct drm_connector *connector,
struct drm_connector_state *state,
struct drm_property *property,
@@ -263,6 +362,12 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector,
SDE_ERROR("invalid topology_control: 0x%llX\n", val);
}
+ if (idx == CONNECTOR_PROP_HDR_METADATA) {
+ rc = _sde_connector_set_hdr_info(c_conn, c_state, (void *)val);
+ if (rc)
+ SDE_ERROR_CONN(c_conn, "cannot set hdr info %d\n", rc);
+ }
+
/* check for custom property handling */
if (!rc && c_conn->ops.set_property) {
rc = c_conn->ops.set_property(connector,
@@ -355,6 +460,32 @@ void sde_connector_complete_commit(struct drm_connector *connector)
sde_fence_signal(&to_sde_connector(connector)->retire_fence, 0);
}
+static void sde_connector_update_hdr_props(struct drm_connector *connector)
+{
+ struct sde_connector *c_conn = to_sde_connector(connector);
+ struct drm_msm_ext_panel_hdr_properties hdr_prop = {};
+
+ hdr_prop.hdr_supported = connector->hdr_supported;
+
+ if (hdr_prop.hdr_supported) {
+ hdr_prop.hdr_eotf =
+ connector->hdr_eotf;
+ hdr_prop.hdr_metadata_type_one =
+ connector->hdr_metadata_type_one;
+ hdr_prop.hdr_max_luminance =
+ connector->hdr_max_luminance;
+ hdr_prop.hdr_avg_luminance =
+ connector->hdr_avg_luminance;
+ hdr_prop.hdr_min_luminance =
+ connector->hdr_min_luminance;
+ }
+ msm_property_set_blob(&c_conn->property_info,
+ &c_conn->blob_hdr,
+ &hdr_prop,
+ sizeof(hdr_prop),
+ CONNECTOR_PROP_HDR_INFO);
+}
+
static enum drm_connector_status
sde_connector_detect(struct drm_connector *connector, bool force)
{
@@ -392,6 +523,7 @@ static const struct drm_connector_funcs sde_connector_ops = {
static int sde_connector_get_modes(struct drm_connector *connector)
{
struct sde_connector *c_conn;
+ int ret = 0;
if (!connector) {
SDE_ERROR("invalid connector\n");
@@ -403,8 +535,11 @@ static int sde_connector_get_modes(struct drm_connector *connector)
SDE_DEBUG("missing get_modes callback\n");
return 0;
}
+ ret = c_conn->ops.get_modes(connector, c_conn->display);
+ if (ret)
+ sde_connector_update_hdr_props(connector);
- return c_conn->ops.get_modes(connector, c_conn->display);
+ return ret;
}
static enum drm_mode_status
@@ -575,6 +710,17 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
kfree(info);
}
+ if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
+ msm_property_install_blob(&c_conn->property_info,
+ "hdr_properties",
+ DRM_MODE_PROP_IMMUTABLE,
+ CONNECTOR_PROP_HDR_INFO);
+ }
+
+ msm_property_install_volatile_range(&c_conn->property_info,
+ "hdr_metadata", 0x0, 0, ~0, 0,
+ CONNECTOR_PROP_HDR_METADATA);
+
msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE",
0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE);
@@ -582,6 +728,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
"PLL_DELTA", 0x0, INT_MIN, INT_MAX, 0,
CONNECTOR_PROP_PLL_DELTA);
+ msm_property_install_volatile_range(&c_conn->property_info,
+ "PLL_ENABLE", 0x0, 0, 1, 0,
+ CONNECTOR_PROP_PLL_ENABLE);
+
/* enum/bitmask properties */
msm_property_install_enum(&c_conn->property_info, "topology_name",
DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name,
@@ -608,6 +758,8 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
error_destroy_property:
if (c_conn->blob_caps)
drm_property_unreference_blob(c_conn->blob_caps);
+ if (c_conn->blob_hdr)
+ drm_property_unreference_blob(c_conn->blob_hdr);
msm_property_destroy(&c_conn->property_info);
error_unregister_conn:
drm_connector_unregister(&c_conn->base);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 3f26ee7d5965..19e2b8a3e41c 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -122,6 +122,19 @@ struct sde_connector_ops {
int (*get_info)(struct msm_display_info *info, void *display);
int (*set_backlight)(void *display, u32 bl_lvl);
+
+
+ /**
+ * pre_kickoff - trigger display to program kickoff-time features
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display structure
+ * @params: Parameter bundle of connector-stored information for
+ * kickoff-time programming into the display
+ * Returns: Zero on success
+ */
+ int (*pre_kickoff)(struct drm_connector *connector,
+ void *display,
+ struct msm_display_kickoff_params *params);
};
/**
@@ -139,6 +152,7 @@ struct sde_connector_ops {
* @property_info: Private structure for generic property handling
* @property_data: Array of private data for generic property handling
* @blob_caps: Pointer to blob structure for 'capabilities' property
+ * @blob_hdr: Pointer to blob structure for 'hdr_properties' property
*/
struct sde_connector {
struct drm_connector base;
@@ -159,6 +173,7 @@ struct sde_connector {
struct msm_property_info property_info;
struct msm_property_data property_data[CONNECTOR_PROP_COUNT];
struct drm_property_blob *blob_caps;
+ struct drm_property_blob *blob_hdr;
};
/**
@@ -206,12 +221,14 @@ struct sde_connector {
* @out_fb: Pointer to output frame buffer, if applicable
* @aspace: Address space for accessing frame buffer objects, if applicable
* @property_values: Local cache of current connector property values
+ * @hdr_meta: HDR metadata info passed from userspace
*/
struct sde_connector_state {
struct drm_connector_state base;
struct drm_framebuffer *out_fb;
struct msm_gem_address_space *aspace;
uint64_t property_values[CONNECTOR_PROP_COUNT];
+ struct drm_msm_ext_panel_hdr_metadata hdr_meta;
};
/**
@@ -303,5 +320,12 @@ void sde_connector_complete_commit(struct drm_connector *connector);
int sde_connector_get_info(struct drm_connector *connector,
struct msm_display_info *info);
+/**
+ * sde_connector_pre_kickoff - trigger kickoff time feature programming
+ * @connector: Pointer to drm connector object
+ * Returns: Zero on success
+ */
+int sde_connector_pre_kickoff(struct drm_connector *connector);
+
#endif /* _SDE_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index adfe9e54e6f4..7d2291dadc18 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -36,6 +36,7 @@
#include "sde_connector.h"
#include "sde_power_handle.h"
#include "sde_core_perf.h"
+#include "sde_trace.h"
/* default input fence timeout, in ms */
#define SDE_CRTC_INPUT_FENCE_TIMEOUT 2000
@@ -57,6 +58,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 +185,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 +194,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 +245,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 */
@@ -342,12 +368,6 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc,
cstate->is_rt = true;
}
- if (cstate->num_connectors > 0 && cstate->connectors[0]->encoder)
- cstate->intf_mode = sde_encoder_get_intf_mode(
- cstate->connectors[0]->encoder);
- else
- cstate->intf_mode = INTF_MODE_NONE;
-
/* prepare main output fence */
sde_fence_prepare(&sde_crtc->output_fence);
}
@@ -398,6 +418,22 @@ static void _sde_crtc_complete_flip(struct drm_crtc *crtc,
spin_unlock_irqrestore(&dev->event_lock, flags);
}
+enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc)
+{
+ struct drm_encoder *encoder;
+
+ if (!crtc || !crtc->dev) {
+ SDE_ERROR("invalid crtc\n");
+ return INTF_MODE_NONE;
+ }
+
+ drm_for_each_encoder(encoder, crtc->dev)
+ if (encoder->crtc == crtc)
+ return sde_encoder_get_intf_mode(encoder);
+
+ return INTF_MODE_NONE;
+}
+
static void sde_crtc_vblank_cb(void *data)
{
struct drm_crtc *crtc = (struct drm_crtc *)data;
@@ -617,6 +653,7 @@ static void _sde_crtc_wait_for_fences(struct drm_crtc *crtc)
* that each plane can check its fence status and react appropriately
* if its fence has timed out.
*/
+ SDE_ATRACE_BEGIN("plane_wait_input_fence");
drm_atomic_crtc_for_each_plane(plane, crtc) {
if (wait_ms) {
/* determine updated wait time */
@@ -628,6 +665,7 @@ static void _sde_crtc_wait_for_fences(struct drm_crtc *crtc)
}
sde_plane_wait_input_fence(plane, wait_ms);
}
+ SDE_ATRACE_END("plane_wait_input_fence");
}
static void _sde_crtc_setup_mixer_for_encoder(
@@ -862,6 +900,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
sde_kms = _sde_crtc_get_kms(crtc);
priv = sde_kms->dev->dev_private;
+ SDE_ATRACE_BEGIN("crtc_commit");
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc)
continue;
@@ -878,7 +917,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
SDE_ERROR("crtc%d invalid frame pending\n",
crtc->base.id);
SDE_EVT32(DRMID(crtc), 0);
- return;
+ goto end;
} else if (atomic_inc_return(&sde_crtc->frame_pending) == 1) {
/* acquire bandwidth and other resources */
SDE_DEBUG("crtc%d first commit\n", crtc->base.id);
@@ -896,6 +935,9 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
sde_encoder_kickoff(encoder);
}
+end:
+ SDE_ATRACE_END("crtc_commit");
+ return;
}
/**
@@ -1256,7 +1298,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 +1318,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 +1367,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);
@@ -1622,7 +1675,7 @@ static int sde_crtc_debugfs_state_show(struct seq_file *s, void *v)
seq_printf(s, "num_connectors: %d\n", cstate->num_connectors);
seq_printf(s, "is_rt: %d\n", cstate->is_rt);
- seq_printf(s, "intf_mode: %d\n", cstate->intf_mode);
+ seq_printf(s, "intf_mode: %d\n", sde_crtc_get_intf_mode(crtc));
seq_printf(s, "bw_ctl: %llu\n", cstate->cur_perf.bw_ctl);
seq_printf(s, "core_clk_rate: %u\n", cstate->cur_perf.core_clk_rate);
seq_printf(s, "max_per_pipe_ib: %llu\n",
@@ -1665,7 +1718,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_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 97a20b987ef5..aaa815c76c4e 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -246,16 +246,10 @@ void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
bool sde_crtc_is_rt(struct drm_crtc *crtc);
/**
- * sde_crtc_get_intf_mode - get interface mode of the given crtc
+ * sde_crtc_get_intf_mode - get primary interface mode of the given crtc
* @crtc: Pointert to crtc
*/
-static inline enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc)
-{
- struct sde_crtc_state *cstate =
- crtc ? to_sde_crtc_state(crtc->state) : NULL;
-
- return cstate ? cstate->intf_mode : INTF_MODE_NONE;
-}
+enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc);
/**
* sde_core_perf_crtc_is_wb - check if writeback is primary output of this crtc
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 030b192e5df4..29444a83cf02 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -32,6 +32,7 @@
#include "sde_formats.h"
#include "sde_encoder_phys.h"
#include "sde_color_processing.h"
+#include "sde_trace.h"
#define SDE_DEBUG_ENC(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\
(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
@@ -514,6 +515,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc,
if (!drm_enc || !phy_enc)
return;
+ SDE_ATRACE_BEGIN("encoder_vblank_callback");
sde_enc = to_sde_encoder_virt(drm_enc);
spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
@@ -522,6 +524,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc,
spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
atomic_inc(&phy_enc->vsync_cnt);
+ SDE_ATRACE_END("encoder_vblank_callback");
}
static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc,
@@ -530,8 +533,10 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc,
if (!phy_enc)
return;
+ SDE_ATRACE_BEGIN("encoder_underrun_callback");
atomic_inc(&phy_enc->underrun_cnt);
SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt));
+ SDE_ATRACE_END("encoder_underrun_callback");
}
void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,
@@ -795,6 +800,7 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
struct sde_encoder_virt *sde_enc;
struct sde_encoder_phys *phys;
unsigned int i;
+ int rc;
if (!drm_enc) {
SDE_ERROR("invalid encoder\n");
@@ -811,6 +817,14 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
if (phys && phys->ops.prepare_for_kickoff)
phys->ops.prepare_for_kickoff(phys);
}
+
+ if (sde_enc->cur_master && sde_enc->cur_master->connector) {
+ rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector);
+ if (rc)
+ SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n",
+ sde_enc->cur_master->connector->base.id,
+ rc);
+ }
}
void sde_encoder_kickoff(struct drm_encoder *drm_enc)
@@ -823,6 +837,7 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc)
SDE_ERROR("invalid encoder\n");
return;
}
+ SDE_ATRACE_BEGIN("encoder_kickoff");
sde_enc = to_sde_encoder_virt(drm_enc);
SDE_DEBUG_ENC(sde_enc, "\n");
@@ -842,6 +857,7 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc)
if (phys && phys->ops.handle_post_kickoff)
phys->ops.handle_post_kickoff(phys);
}
+ SDE_ATRACE_END("encoder_kickoff");
}
static int _sde_encoder_status_show(struct seq_file *s, void *data)
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 9368c4974126..a48962a2384b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -28,6 +28,24 @@
#define WBID(wb_enc) ((wb_enc) ? wb_enc->wb_dev->wb_idx : -1)
+#define TO_S15D16(_x_) ((_x_) << 7)
+
+/**
+ * sde_rgb2yuv_601l - rgb to yuv color space conversion matrix
+ *
+ */
+static struct sde_csc_cfg sde_encoder_phys_wb_rgb2yuv_601l = {
+ {
+ TO_S15D16(0x0083), TO_S15D16(0x0102), TO_S15D16(0x0032),
+ TO_S15D16(0x1fb5), TO_S15D16(0x1f6c), TO_S15D16(0x00e1),
+ TO_S15D16(0x00e1), TO_S15D16(0x1f45), TO_S15D16(0x1fdc)
+ },
+ { 0x00, 0x00, 0x00 },
+ { 0x0040, 0x0200, 0x0200 },
+ { 0x000, 0x3ff, 0x000, 0x3ff, 0x000, 0x3ff },
+ { 0x040, 0x3ac, 0x040, 0x3c0, 0x040, 0x3c0 },
+};
+
/**
* sde_encoder_phys_wb_is_master - report wb always as master encoder
*/
@@ -150,6 +168,15 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
cdm_cfg->h_cdwn_type,
cdm_cfg->v_cdwn_type);
+ if (hw_cdm && hw_cdm->ops.setup_csc_data) {
+ ret = hw_cdm->ops.setup_csc_data(hw_cdm,
+ &sde_encoder_phys_wb_rgb2yuv_601l);
+ if (ret < 0) {
+ SDE_ERROR("failed to setup CSC %d\n", ret);
+ return;
+ }
+ }
+
if (hw_cdm && hw_cdm->ops.setup_cdwn) {
ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg);
if (ret < 0) {
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index a59ec31ba276..2187d221a352 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -10,13 +10,18 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
+
#include <uapi/drm/drm_fourcc.h>
+#include <uapi/media/msm_media_info.h>
#include "sde_kms.h"
#include "sde_formats.h"
#define SDE_UBWC_META_MACRO_W_H 16
#define SDE_UBWC_META_BLOCK_SIZE 256
+#define SDE_UBWC_PLANE_SIZE_ALIGNMENT 4096
+
#define SDE_MAX_IMG_WIDTH 0x3FFF
#define SDE_MAX_IMG_HEIGHT 0x3FFF
@@ -42,7 +47,7 @@ bp, flg, fm, np) \
.unpack_count = uc, \
.bpp = bp, \
.fetch_mode = fm, \
- .flag = flg, \
+ .flag = {(flg)}, \
.num_planes = np \
}
@@ -60,7 +65,7 @@ alpha, chroma, count, bp, flg, fm, np) \
.unpack_count = count, \
.bpp = bp, \
.fetch_mode = fm, \
- .flag = flg, \
+ .flag = {(flg)}, \
.num_planes = np \
}
@@ -77,7 +82,24 @@ alpha, chroma, count, bp, flg, fm, np) \
.unpack_count = 2, \
.bpp = 2, \
.fetch_mode = fm, \
- .flag = flg, \
+ .flag = {(flg)}, \
+ .num_planes = np \
+}
+
+#define PSEUDO_YUV_FMT_LOOSE(fmt, a, r, g, b, e0, e1, chroma, flg, fm, np)\
+{ \
+ .base.pixel_format = DRM_FORMAT_ ## fmt, \
+ .fetch_planes = SDE_PLANE_PSEUDO_PLANAR, \
+ .alpha_enable = false, \
+ .element = { (e0), (e1), 0, 0 }, \
+ .bits = { g, b, r, a }, \
+ .chroma_sample = chroma, \
+ .unpack_align_msb = 1, \
+ .unpack_tight = 0, \
+ .unpack_count = 2, \
+ .bpp = 2, \
+ .fetch_mode = fm, \
+ .flag = {(flg)}, \
.num_planes = np \
}
@@ -95,10 +117,20 @@ flg, fm, np) \
.unpack_count = 1, \
.bpp = bp, \
.fetch_mode = fm, \
- .flag = flg, \
+ .flag = {(flg)}, \
.num_planes = np \
}
+/*
+ * struct sde_media_color_map - maps drm format to media format
+ * @format: DRM base pixel format
+ * @color: Media API color related to DRM format
+ */
+struct sde_media_color_map {
+ uint32_t format;
+ uint32_t color;
+};
+
static const struct sde_format sde_format_map[] = {
INTERLEAVED_RGB_FMT(ARGB8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
@@ -270,49 +302,49 @@ static const struct sde_format sde_format_map[] = {
INTERLEAVED_RGB_FMT(BGRA1010102,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
true, 4, SDE_FORMAT_FLAG_DX,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBA1010102,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
true, 4, SDE_FORMAT_FLAG_DX,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ABGR2101010,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 4, SDE_FORMAT_FLAG_DX,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ARGB2101010,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
true, 4, SDE_FORMAT_FLAG_DX,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XRGB2101010,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
false, 4, SDE_FORMAT_FLAG_DX,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRX1010102,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
false, 4, SDE_FORMAT_FLAG_DX,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XBGR2101010,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
false, 4, SDE_FORMAT_FLAG_DX,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBX1010102,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
false, 4, SDE_FORMAT_FLAG_DX,
SDE_FETCH_LINEAR, 1),
@@ -378,46 +410,149 @@ static const struct sde_format sde_format_map[] = {
};
/*
+ * A5x tile formats tables:
+ * These tables hold the A5x tile formats supported.
+ */
+static const struct sde_format sde_format_map_tile[] = {
+ INTERLEAVED_RGB_FMT(ARGB8888,
+ COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ true, 4, 0,
+ SDE_FETCH_UBWC, 1),
+
+ INTERLEAVED_RGB_FMT(ABGR8888,
+ COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ true, 4, 0,
+ SDE_FETCH_UBWC, 1),
+
+ INTERLEAVED_RGB_FMT(RGBA8888,
+ COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ true, 4, 0,
+ SDE_FETCH_UBWC, 1),
+
+ INTERLEAVED_RGB_FMT(BGRA8888,
+ COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ true, 4, 0,
+ SDE_FETCH_UBWC, 1),
+
+ INTERLEAVED_RGB_FMT(BGRX8888,
+ COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ false, 4, 0,
+ SDE_FETCH_UBWC, 1),
+
+ INTERLEAVED_RGB_FMT(XRGB8888,
+ COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ false, 4, 0,
+ SDE_FETCH_UBWC, 1),
+
+ INTERLEAVED_RGB_FMT(RGBX8888,
+ COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ false, 4, 0,
+ SDE_FETCH_UBWC, 1),
+
+ PSEUDO_YUV_FMT(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV,
+ SDE_FETCH_UBWC, 2),
+
+ PSEUDO_YUV_FMT(NV21,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C2_R_Cr, C1_B_Cb,
+ SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV,
+ SDE_FETCH_UBWC, 2),
+};
+
+static const struct sde_format sde_format_map_p010_tile[] = {
+ PSEUDO_YUV_FMT_LOOSE(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX),
+ SDE_FETCH_UBWC, 2),
+};
+
+static const struct sde_format sde_format_map_tp10_tile[] = {
+ PSEUDO_YUV_FMT(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX),
+ SDE_FETCH_UBWC, 2),
+};
+
+/*
* UBWC formats table:
* This table holds the UBWC formats supported.
* If a compression ratio needs to be used for this or any other format,
* the data will be passed by user-space.
*/
static const struct sde_format sde_format_map_ubwc[] = {
- INTERLEAVED_RGB_FMT(RGB565,
+ INTERLEAVED_RGB_FMT(BGR565,
0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
- false, 2, 0,
+ false, 2, SDE_FORMAT_FLAG_COMPRESSED,
SDE_FETCH_UBWC, 2),
- INTERLEAVED_RGB_FMT(RGBA8888,
+ INTERLEAVED_RGB_FMT(ABGR8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
- true, 4, 0,
+ true, 4, SDE_FORMAT_FLAG_COMPRESSED,
SDE_FETCH_UBWC, 2),
- INTERLEAVED_RGB_FMT(RGBX8888,
+ INTERLEAVED_RGB_FMT(XBGR8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
- false, 4, 0,
+ false, 4, SDE_FORMAT_FLAG_COMPRESSED,
SDE_FETCH_UBWC, 2),
- INTERLEAVED_RGB_FMT(RGBA1010102,
+ INTERLEAVED_RGB_FMT(ABGR2101010,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
- true, 4, SDE_FORMAT_FLAG_DX,
+ true, 4, SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED,
SDE_FETCH_UBWC, 2),
- INTERLEAVED_RGB_FMT(RGBX1010102,
+ INTERLEAVED_RGB_FMT(XBGR2101010,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
- true, 4, SDE_FORMAT_FLAG_DX,
+ true, 4, SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED,
SDE_FETCH_UBWC, 2),
PSEUDO_YUV_FMT(NV12,
0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C1_B_Cb, C2_R_Cr,
- SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV,
+ SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV |
+ SDE_FORMAT_FLAG_COMPRESSED,
+ SDE_FETCH_UBWC, 4),
+};
+
+static const struct sde_format sde_format_map_p010[] = {
+ PSEUDO_YUV_FMT_LOOSE(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX),
+ SDE_FETCH_LINEAR, 2),
+};
+
+static const struct sde_format sde_format_map_p010_ubwc[] = {
+ PSEUDO_YUV_FMT_LOOSE(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX |
+ SDE_FORMAT_FLAG_COMPRESSED),
+ SDE_FETCH_UBWC, 4),
+};
+
+static const struct sde_format sde_format_map_tp10_ubwc[] = {
+ PSEUDO_YUV_FMT(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX |
+ SDE_FORMAT_FLAG_COMPRESSED),
SDE_FETCH_UBWC, 4),
};
@@ -452,6 +587,37 @@ static void _sde_get_v_h_subsample_rate(
}
}
+static int _sde_format_get_media_color_ubwc(const struct sde_format *fmt)
+{
+ static const struct sde_media_color_map sde_media_ubwc_map[] = {
+ {DRM_FORMAT_ABGR8888, COLOR_FMT_RGBA8888_UBWC},
+ {DRM_FORMAT_XBGR8888, COLOR_FMT_RGBA8888_UBWC},
+ {DRM_FORMAT_ABGR2101010, COLOR_FMT_RGBA1010102_UBWC},
+ {DRM_FORMAT_XBGR2101010, COLOR_FMT_RGBA1010102_UBWC},
+ {DRM_FORMAT_BGR565, COLOR_FMT_RGB565_UBWC},
+ };
+ int color_fmt = -1;
+ int i;
+
+ if (fmt->base.pixel_format == DRM_FORMAT_NV12) {
+ if (SDE_FORMAT_IS_DX(fmt)) {
+ if (fmt->unpack_tight)
+ color_fmt = COLOR_FMT_NV12_BPP10_UBWC;
+ else
+ color_fmt = COLOR_FMT_P010_UBWC;
+ } else
+ color_fmt = COLOR_FMT_NV12_UBWC;
+ return color_fmt;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sde_media_ubwc_map); ++i)
+ if (fmt->base.pixel_format == sde_media_ubwc_map[i].format) {
+ color_fmt = sde_media_ubwc_map[i].color;
+ break;
+ }
+ return color_fmt;
+}
+
static int _sde_format_get_plane_sizes_ubwc(
const struct sde_format *fmt,
const uint32_t width,
@@ -459,6 +625,8 @@ static int _sde_format_get_plane_sizes_ubwc(
struct sde_hw_fmt_layout *layout)
{
int i;
+ int color;
+ bool meta = SDE_FORMAT_IS_UBWC(fmt);
memset(layout, 0, sizeof(struct sde_hw_fmt_layout));
layout->format = fmt;
@@ -466,86 +634,63 @@ static int _sde_format_get_plane_sizes_ubwc(
layout->height = height;
layout->num_planes = fmt->num_planes;
- if (fmt->base.pixel_format == DRM_FORMAT_NV12) {
- uint32_t y_stride_alignment, uv_stride_alignment;
- uint32_t y_height_alignment, uv_height_alignment;
- uint32_t y_tile_width = 32;
- uint32_t y_tile_height = 8;
- uint32_t uv_tile_width = y_tile_width / 2;
- uint32_t uv_tile_height = y_tile_height;
- uint32_t y_bpp_numer = 1, y_bpp_denom = 1;
- uint32_t uv_bpp_numer = 1, uv_bpp_denom = 1;
-
- y_stride_alignment = 128;
- uv_stride_alignment = 64;
- y_height_alignment = 32;
- uv_height_alignment = 32;
- y_bpp_numer = 1;
- uv_bpp_numer = 2;
- y_bpp_denom = 1;
- uv_bpp_denom = 1;
-
- layout->num_planes = 4;
- /* Y bitstream stride and plane size */
- layout->plane_pitch[0] = ALIGN(width, y_stride_alignment);
- layout->plane_pitch[0] = (layout->plane_pitch[0] * y_bpp_numer)
- / y_bpp_denom;
- layout->plane_size[0] = ALIGN(layout->plane_pitch[0] *
- ALIGN(height, y_height_alignment), 4096);
-
- /* CbCr bitstream stride and plane size */
- layout->plane_pitch[1] = ALIGN(width / 2, uv_stride_alignment);
- layout->plane_pitch[1] = (layout->plane_pitch[1] * uv_bpp_numer)
- / uv_bpp_denom;
- layout->plane_size[1] = ALIGN(layout->plane_pitch[1] *
- ALIGN(height / 2, uv_height_alignment), 4096);
-
- /* Y meta data stride and plane size */
- layout->plane_pitch[2] = ALIGN(
- DIV_ROUND_UP(width, y_tile_width), 64);
- layout->plane_size[2] = ALIGN(layout->plane_pitch[2] *
- ALIGN(DIV_ROUND_UP(height, y_tile_height), 16), 4096);
-
- /* CbCr meta data stride and plane size */
- layout->plane_pitch[3] = ALIGN(
- DIV_ROUND_UP(width / 2, uv_tile_width), 64);
- layout->plane_size[3] = ALIGN(layout->plane_pitch[3] *
- ALIGN(DIV_ROUND_UP(height / 2, uv_tile_height), 16),
- 4096);
-
- } else if (fmt->base.pixel_format == DRM_FORMAT_ABGR8888 ||
- fmt->base.pixel_format == DRM_FORMAT_XBGR8888 ||
- fmt->base.pixel_format == DRM_FORMAT_BGRA1010102 ||
- fmt->base.pixel_format == DRM_FORMAT_BGRX1010102 ||
- fmt->base.pixel_format == DRM_FORMAT_BGR565) {
-
- uint32_t stride_alignment, aligned_bitstream_width;
-
- if (fmt->base.pixel_format == DRM_FORMAT_BGR565)
- stride_alignment = 128;
- else
- stride_alignment = 64;
- layout->num_planes = 3;
-
- /* Nothing in plane[1] */
-
- /* RGB bitstream stride and plane size */
- aligned_bitstream_width = ALIGN(width, stride_alignment);
- layout->plane_pitch[0] = aligned_bitstream_width * fmt->bpp;
- layout->plane_size[0] = ALIGN(fmt->bpp * aligned_bitstream_width
- * ALIGN(height, 16), 4096);
-
- /* RGB meta data stride and plane size */
- layout->plane_pitch[2] = ALIGN(DIV_ROUND_UP(
- aligned_bitstream_width, 16), 64);
- layout->plane_size[2] = ALIGN(layout->plane_pitch[2] *
- ALIGN(DIV_ROUND_UP(height, 4), 16), 4096);
- } else {
+ color = _sde_format_get_media_color_ubwc(fmt);
+ if (color < 0) {
DRM_ERROR("UBWC format not supported for fmt:0x%X\n",
fmt->base.pixel_format);
return -EINVAL;
}
+ if (SDE_FORMAT_IS_YUV(layout->format)) {
+ uint32_t y_sclines, uv_sclines;
+ uint32_t y_meta_scanlines = 0;
+ uint32_t uv_meta_scanlines = 0;
+
+ layout->num_planes = 2;
+ layout->plane_pitch[0] = VENUS_Y_STRIDE(color, width);
+ y_sclines = VENUS_Y_SCANLINES(color, height);
+ layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] *
+ y_sclines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ layout->plane_pitch[1] = VENUS_UV_STRIDE(color, width);
+ uv_sclines = VENUS_UV_SCANLINES(color, height);
+ layout->plane_size[1] = MSM_MEDIA_ALIGN(layout->plane_pitch[1] *
+ uv_sclines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ if (!meta)
+ goto done;
+
+ layout->num_planes += 2;
+ layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, width);
+ y_meta_scanlines = VENUS_Y_META_SCANLINES(color, height);
+ layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] *
+ y_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ layout->plane_pitch[3] = VENUS_UV_META_STRIDE(color, width);
+ uv_meta_scanlines = VENUS_UV_META_SCANLINES(color, height);
+ layout->plane_size[3] = MSM_MEDIA_ALIGN(layout->plane_pitch[3] *
+ uv_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ } else {
+ uint32_t rgb_scanlines, rgb_meta_scanlines;
+
+ layout->num_planes = 1;
+
+ layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, width);
+ rgb_scanlines = VENUS_RGB_SCANLINES(color, height);
+ layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] *
+ rgb_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ if (!meta)
+ goto done;
+ layout->num_planes += 2;
+ layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, width);
+ rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, height);
+ layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] *
+ rgb_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+ }
+
+done:
for (i = 0; i < SDE_MAX_PLANES; i++)
layout->total_size += layout->plane_size[i];
@@ -574,6 +719,7 @@ static int _sde_format_get_plane_sizes_linear(
} else {
uint32_t v_subsample, h_subsample;
uint32_t chroma_samp;
+ uint32_t bpp = 1;
chroma_samp = fmt->chroma_sample;
_sde_get_v_h_subsample_rate(chroma_samp, &v_subsample,
@@ -584,8 +730,11 @@ static int _sde_format_get_plane_sizes_linear(
return -EINVAL;
}
- layout->plane_pitch[0] = width;
- layout->plane_pitch[1] = width / h_subsample;
+ if ((fmt->base.pixel_format == DRM_FORMAT_NV12) &&
+ (SDE_FORMAT_IS_DX(fmt)))
+ bpp = 2;
+ layout->plane_pitch[0] = width * bpp;
+ layout->plane_pitch[1] = layout->plane_pitch[0] / h_subsample;
layout->plane_size[0] = layout->plane_pitch[0] * height;
layout->plane_size[1] = layout->plane_pitch[1] *
(height / v_subsample);
@@ -608,7 +757,7 @@ static int _sde_format_get_plane_sizes_linear(
return 0;
}
-static int _sde_format_get_plane_sizes(
+int sde_format_get_plane_sizes(
const struct sde_format *fmt,
const uint32_t w,
const uint32_t h,
@@ -624,7 +773,7 @@ static int _sde_format_get_plane_sizes(
return -ERANGE;
}
- if (SDE_FORMAT_IS_UBWC(fmt))
+ if (SDE_FORMAT_IS_UBWC(fmt) || SDE_FORMAT_IS_TILE(fmt))
return _sde_format_get_plane_sizes_ubwc(fmt, w, h, layout);
return _sde_format_get_plane_sizes_linear(fmt, w, h, layout);
@@ -636,6 +785,7 @@ static int _sde_format_populate_addrs_ubwc(
struct sde_hw_fmt_layout *layout)
{
uint32_t base_addr;
+ bool meta;
if (!fb || !layout) {
DRM_ERROR("invalid pointers\n");
@@ -648,6 +798,8 @@ static int _sde_format_populate_addrs_ubwc(
return -EFAULT;
}
+ meta = SDE_FORMAT_IS_UBWC(layout->format);
+
/* Per-format logic for verifying active planes */
if (SDE_FORMAT_IS_YUV(layout->format)) {
/************************************************/
@@ -677,6 +829,9 @@ static int _sde_format_populate_addrs_ubwc(
layout->plane_addr[1] = base_addr + layout->plane_size[0]
+ layout->plane_size[2] + layout->plane_size[3];
+ if (!meta)
+ goto done;
+
/* configure Y metadata plane */
layout->plane_addr[2] = base_addr;
@@ -704,10 +859,14 @@ static int _sde_format_populate_addrs_ubwc(
layout->plane_addr[0] = base_addr + layout->plane_size[2];
layout->plane_addr[1] = 0;
+
+ if (!meta)
+ goto done;
+
layout->plane_addr[2] = base_addr;
layout->plane_addr[3] = 0;
}
-
+done:
return 0;
}
@@ -761,7 +920,7 @@ int sde_format_populate_layout(
layout->format = to_sde_format(msm_framebuffer_format(fb));
/* Populate the plane sizes etc via get_format */
- ret = _sde_format_get_plane_sizes(layout->format, fb->width, fb->height,
+ ret = sde_format_get_plane_sizes(layout->format, fb->width, fb->height,
layout);
if (ret)
return ret;
@@ -770,7 +929,8 @@ int sde_format_populate_layout(
plane_addr[i] = layout->plane_addr[i];
/* Populate the addresses given the fb */
- if (SDE_FORMAT_IS_UBWC(layout->format))
+ if (SDE_FORMAT_IS_UBWC(layout->format) ||
+ SDE_FORMAT_IS_TILE(layout->format))
ret = _sde_format_populate_addrs_ubwc(aspace, fb, layout);
else
ret = _sde_format_populate_addrs_linear(aspace, fb, layout);
@@ -864,7 +1024,7 @@ int sde_format_check_modified_format(
fmt = to_sde_format(msm_fmt);
num_base_fmt_planes = drm_format_num_planes(fmt->base.pixel_format);
- ret = _sde_format_get_plane_sizes(fmt, cmd->width, cmd->height,
+ ret = sde_format_get_plane_sizes(fmt, cmd->width, cmd->height,
&layout);
if (ret)
return ret;
@@ -874,7 +1034,8 @@ int sde_format_check_modified_format(
DRM_ERROR("invalid handle for plane %d\n", i);
return -EINVAL;
}
- bos_total_size += bos[i]->size;
+ if ((i == 0) || (bos[i] != bos[0]))
+ bos_total_size += bos[i]->size;
}
if (bos_total_size < layout.total_size) {
@@ -902,14 +1063,14 @@ const struct sde_format *sde_get_sde_format_ext(
* All planes used must specify the same modifier.
*/
if (modifiers_len && !modifiers) {
- DRM_ERROR("invalid modifiers array\n");
+ SDE_ERROR("invalid modifiers array\n");
return NULL;
} else if (modifiers && modifiers_len && modifiers[0]) {
mod0 = modifiers[0];
- DBG("plane format modifier 0x%llX", mod0);
+ SDE_DEBUG("plane format modifier 0x%llX\n", mod0);
for (i = 1; i < modifiers_len; i++) {
if (modifiers[i] != mod0) {
- DRM_ERROR("bad fmt mod 0x%llX on plane %d\n",
+ SDE_ERROR("bad fmt mod 0x%llX on plane %d\n",
modifiers[i], i);
return NULL;
}
@@ -922,12 +1083,55 @@ const struct sde_format *sde_get_sde_format_ext(
map_size = ARRAY_SIZE(sde_format_map);
break;
case DRM_FORMAT_MOD_QCOM_COMPRESSED:
+ case DRM_FORMAT_MOD_QCOM_COMPRESSED | DRM_FORMAT_MOD_QCOM_TILE:
map = sde_format_map_ubwc;
map_size = ARRAY_SIZE(sde_format_map_ubwc);
- DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED", format);
+ SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED\n",
+ format);
+ break;
+ case DRM_FORMAT_MOD_QCOM_DX:
+ map = sde_format_map_p010;
+ map_size = ARRAY_SIZE(sde_format_map_p010);
+ SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_DX\n", format);
+ break;
+ case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED):
+ case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED |
+ DRM_FORMAT_MOD_QCOM_TILE):
+ map = sde_format_map_p010_ubwc;
+ map_size = ARRAY_SIZE(sde_format_map_p010_ubwc);
+ SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX\n",
+ format);
+ break;
+ case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED |
+ DRM_FORMAT_MOD_QCOM_TIGHT):
+ case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED |
+ DRM_FORMAT_MOD_QCOM_TIGHT | DRM_FORMAT_MOD_QCOM_TILE):
+ map = sde_format_map_tp10_ubwc;
+ map_size = ARRAY_SIZE(sde_format_map_tp10_ubwc);
+ SDE_DEBUG(
+ "found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX/TIGHT\n",
+ format);
+ break;
+ case DRM_FORMAT_MOD_QCOM_TILE:
+ map = sde_format_map_tile;
+ map_size = ARRAY_SIZE(sde_format_map_tile);
+ SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE\n", format);
+ break;
+ case (DRM_FORMAT_MOD_QCOM_TILE | DRM_FORMAT_MOD_QCOM_DX):
+ map = sde_format_map_p010_tile;
+ map_size = ARRAY_SIZE(sde_format_map_p010_tile);
+ SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE/DX\n",
+ format);
+ break;
+ case (DRM_FORMAT_MOD_QCOM_TILE | DRM_FORMAT_MOD_QCOM_DX |
+ DRM_FORMAT_MOD_QCOM_TIGHT):
+ map = sde_format_map_tp10_tile;
+ map_size = ARRAY_SIZE(sde_format_map_tp10_tile);
+ SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE/DX/TIGHT\n",
+ format);
break;
default:
- DRM_ERROR("unsupported format modifier %llX\n", mod0);
+ SDE_ERROR("unsupported format modifier %llX\n", mod0);
return NULL;
}
@@ -939,10 +1143,10 @@ const struct sde_format *sde_get_sde_format_ext(
}
if (fmt == NULL)
- DRM_ERROR("unsupported fmt 0x%X modifier 0x%llX\n",
+ SDE_ERROR("unsupported fmt 0x%X modifier 0x%llX\n",
format, mod0);
else
- DBG("fmt %s mod 0x%llX ubwc %d yuv %d",
+ SDE_DEBUG("fmt %s mod 0x%llX ubwc %d yuv %d\n",
drm_get_format_name(format), mod0,
SDE_FORMAT_IS_UBWC(fmt),
SDE_FORMAT_IS_YUV(fmt));
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.h b/drivers/gpu/drm/msm/sde/sde_formats.h
index 0de081d619b7..ec8f97da4a41 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.h
+++ b/drivers/gpu/drm/msm/sde/sde_formats.h
@@ -59,6 +59,21 @@ uint32_t sde_populate_formats(
uint32_t pixel_formats_max);
/**
+ * sde_format_get_plane_sizes - calculate size and layout of given buffer format
+ * @fmt: pointer to sde_format
+ * @w: width of the buffer
+ * @h: height of the buffer
+ * @layout: layout of the buffer
+ *
+ * Return: size of the buffer
+ */
+int sde_format_get_plane_sizes(
+ const struct sde_format *fmt,
+ const uint32_t w,
+ const uint32_t h,
+ struct sde_hw_fmt_layout *layout);
+
+/**
* sde_format_check_modified_format - validate format and buffers for
* sde non-standard, i.e. modified format
* @kms: kms driver
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 519288f0dda2..eb398fbee816 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"
@@ -403,6 +405,38 @@ static struct sde_prop_type vbif_prop[] = {
/*************************************************************
* static API list
*************************************************************/
+
+/**
+ * _sde_copy_formats - copy formats from src_list to dst_list
+ * @dst_list: pointer to destination list where to copy formats
+ * @dst_list_size: size of destination list
+ * @dst_list_pos: starting position on the list where to copy formats
+ * @src_list: pointer to source list where to copy formats from
+ * @src_list_size: size of source list
+ * Return: number of elements populated
+ */
+static uint32_t _sde_copy_formats(
+ struct sde_format_extended *dst_list,
+ uint32_t dst_list_size,
+ uint32_t dst_list_pos,
+ const struct sde_format_extended *src_list,
+ uint32_t src_list_size)
+{
+ uint32_t cur_pos, i;
+
+ if (!dst_list || !src_list || (dst_list_pos >= (dst_list_size - 1)))
+ return 0;
+
+ for (i = 0, cur_pos = dst_list_pos;
+ (cur_pos < (dst_list_size - 1)) && (i < src_list_size)
+ && src_list[i].fourcc_format; ++i, ++cur_pos)
+ dst_list[cur_pos] = src_list[i];
+
+ dst_list[cur_pos].fourcc_format = 0;
+
+ return i;
+}
+
static int _parse_dt_u32_handler(struct device_node *np,
char *prop_name, u32 *offsets, int len, bool mandatory)
{
@@ -469,6 +503,7 @@ static int _validate_dt_entry(struct device_node *np,
rc = -EINVAL;
}
*off_count = 0;
+ memset(prop_count, 0, sizeof(int) * prop_size);
return rc;
}
}
@@ -656,7 +691,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
sspp->id = SSPP_VIG0 + *vig_count;
sspp->clk_ctrl = SDE_CLK_CTRL_VIG0 + *vig_count;
- sblk->format_list = plane_formats_yuv;
+ sspp->type = SSPP_TYPE_VIG;
set_bit(SDE_SSPP_QOS, &sspp->features);
(*vig_count)++;
@@ -715,6 +750,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,
@@ -725,7 +761,7 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
sspp->id = SSPP_RGB0 + *rgb_count;
sspp->clk_ctrl = SDE_CLK_CTRL_RGB0 + *rgb_count;
- sblk->format_list = plane_formats;
+ sspp->type = SSPP_TYPE_RGB;
set_bit(SDE_SSPP_QOS, &sspp->features);
(*rgb_count)++;
@@ -753,6 +789,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,
@@ -764,8 +801,9 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg,
sblk->maxdwnscale = SSPP_UNITY_SCALE;
sspp->id = SSPP_CURSOR0 + *cursor_count;
sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count;
- sblk->format_list = plane_formats;
+ sspp->type = SSPP_TYPE_CURSOR;
(*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,
@@ -776,9 +814,10 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg,
sblk->maxdwnscale = SSPP_UNITY_SCALE;
sspp->id = SSPP_DMA0 + *dma_count;
sspp->clk_ctrl = SDE_CLK_CTRL_DMA0 + *dma_count;
- sblk->format_list = plane_formats;
+ sspp->type = SSPP_TYPE_DMA;
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 +1239,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;
@@ -1251,7 +1291,6 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i);
wb->vbif_idx = VBIF_NRT;
wb->len = PROP_VALUE_ACCESS(prop_value, WB_LEN, 0);
- wb->format_list = wb2_formats;
if (!prop_exists[WB_LEN])
wb->len = DEFAULT_SDE_HW_BLOCK_LEN;
sblk->maxlinewidth = sde_cfg->max_wb_linewidth;
@@ -1686,7 +1725,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 +1800,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 +1979,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,8 +2020,124 @@ end:
return rc;
}
-static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
+static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
+ uint32_t hw_rev)
{
+ int i, rc = 0;
+ uint32_t dma_list_size, vig_list_size, wb2_list_size;
+ uint32_t cursor_list_size = 0;
+ struct sde_sspp_sub_blks *sblk;
+ uint32_t index = 0;
+
+ if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_300)) {
+ cursor_list_size = ARRAY_SIZE(cursor_formats);
+ sde_cfg->cursor_formats = kcalloc(cursor_list_size,
+ sizeof(struct sde_format_extended), GFP_KERNEL);
+ if (!sde_cfg->cursor_formats) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ index = _sde_copy_formats(sde_cfg->cursor_formats,
+ cursor_list_size, 0, cursor_formats,
+ ARRAY_SIZE(cursor_formats));
+ }
+
+ dma_list_size = ARRAY_SIZE(plane_formats);
+ vig_list_size = ARRAY_SIZE(plane_formats_yuv);
+ wb2_list_size = ARRAY_SIZE(wb2_formats);
+
+ dma_list_size += ARRAY_SIZE(rgb_10bit_formats);
+ vig_list_size += ARRAY_SIZE(rgb_10bit_formats)
+ + ARRAY_SIZE(tp10_ubwc_formats)
+ + ARRAY_SIZE(p010_formats);
+ wb2_list_size += ARRAY_SIZE(rgb_10bit_formats)
+ + ARRAY_SIZE(tp10_ubwc_formats);
+
+ sde_cfg->dma_formats = kcalloc(dma_list_size,
+ sizeof(struct sde_format_extended), GFP_KERNEL);
+ if (!sde_cfg->dma_formats) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ sde_cfg->vig_formats = kcalloc(vig_list_size,
+ sizeof(struct sde_format_extended), GFP_KERNEL);
+ if (!sde_cfg->vig_formats) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ sde_cfg->wb_formats = kcalloc(wb2_list_size,
+ sizeof(struct sde_format_extended), GFP_KERNEL);
+ if (!sde_cfg->wb_formats) {
+ SDE_ERROR("failed to allocate wb format list\n");
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ index = _sde_copy_formats(sde_cfg->dma_formats, dma_list_size,
+ 0, plane_formats, ARRAY_SIZE(plane_formats));
+ index += _sde_copy_formats(sde_cfg->dma_formats, dma_list_size,
+ index, rgb_10bit_formats,
+ ARRAY_SIZE(rgb_10bit_formats));
+
+ index = _sde_copy_formats(sde_cfg->vig_formats, vig_list_size,
+ 0, plane_formats_yuv, ARRAY_SIZE(plane_formats_yuv));
+ index += _sde_copy_formats(sde_cfg->vig_formats, vig_list_size,
+ index, rgb_10bit_formats,
+ ARRAY_SIZE(rgb_10bit_formats));
+ index += _sde_copy_formats(sde_cfg->vig_formats, vig_list_size,
+ index, p010_formats, ARRAY_SIZE(p010_formats));
+
+ index += _sde_copy_formats(sde_cfg->vig_formats, vig_list_size,
+ index, tp10_ubwc_formats,
+ ARRAY_SIZE(tp10_ubwc_formats));
+
+ index = _sde_copy_formats(sde_cfg->wb_formats, wb2_list_size,
+ 0, wb2_formats, ARRAY_SIZE(wb2_formats));
+ index += _sde_copy_formats(sde_cfg->wb_formats, wb2_list_size,
+ index, rgb_10bit_formats,
+ ARRAY_SIZE(rgb_10bit_formats));
+ index += _sde_copy_formats(sde_cfg->wb_formats, wb2_list_size,
+ index, tp10_ubwc_formats,
+ ARRAY_SIZE(tp10_ubwc_formats));
+
+ for (i = 0; i < sde_cfg->sspp_count; ++i) {
+ struct sde_sspp_cfg *sspp = &sde_cfg->sspp[i];
+
+ sblk = (struct sde_sspp_sub_blks *)sspp->sblk;
+ switch (sspp->type) {
+ case SSPP_TYPE_VIG:
+ sblk->format_list = sde_cfg->vig_formats;
+ break;
+ case SSPP_TYPE_CURSOR:
+ if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_300))
+ sblk->format_list = sde_cfg->cursor_formats;
+ else
+ SDE_ERROR("invalid sspp type %d, xin id %d\n",
+ sspp->type, sspp->xin_id);
+ break;
+ case SSPP_TYPE_DMA:
+ sblk->format_list = sde_cfg->dma_formats;
+ break;
+ default:
+ SDE_ERROR("invalid sspp type %d\n", sspp->type);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+
+ for (i = 0; i < sde_cfg->wb_count; ++i)
+ sde_cfg->wb[i].format_list = sde_cfg->wb_formats;
+
+end:
+ return rc;
+}
+
+static int sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
+{
+ int rc = 0;
+
switch (hw_rev) {
case SDE_HW_VER_170:
case SDE_HW_VER_171:
@@ -1900,15 +2145,20 @@ static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
/* update msm8996 target here */
break;
case SDE_HW_VER_300:
+ case SDE_HW_VER_301:
case SDE_HW_VER_400:
/* update cobalt and skunk target here */
+ rc = sde_hardware_format_caps(sde_cfg, hw_rev);
break;
}
+
+ return rc;
}
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 +2182,28 @@ 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->dma_formats);
+ kfree(sde_cfg->cursor_formats);
+ kfree(sde_cfg->vig_formats);
+ kfree(sde_cfg->wb_formats);
+
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,7 +2261,13 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev)
if (rc)
goto end;
- sde_hardware_caps(sde_cfg, hw_rev);
+ rc = _sde_vp_parse_dt(np, sde_cfg);
+ if (rc)
+ SDE_DEBUG("virtual plane is not supported.\n");
+
+ rc = sde_hardware_caps(sde_cfg, hw_rev);
+ if (rc)
+ goto end;
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..01204df48871 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -42,7 +42,8 @@
#define SDE_HW_VER_170 SDE_HW_VER(1, 7, 0) /* 8996 v1.0 */
#define SDE_HW_VER_171 SDE_HW_VER(1, 7, 1) /* 8996 v2.0 */
#define SDE_HW_VER_172 SDE_HW_VER(1, 7, 2) /* 8996 v3.0 */
-#define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* cobalt v1.0 */
+#define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */
+#define SDE_HW_VER_301 SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */
#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* msmskunk v1.0 */
#define IS_MSMSKUNK_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400)
@@ -57,6 +58,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 +458,16 @@ struct sde_ctl_cfg {
* @sblk: SSPP sub-blocks information
* @xin_id: bus client identifier
* @clk_ctrl clock control identifier
+ * @name source pipe name
+ * @type sspp type identifier
*/
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];
+ u32 type;
};
/**
@@ -608,6 +615,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,
@@ -623,6 +655,10 @@ struct sde_perf_cfg {
* @csc_type csc or csc_10bit support.
* @has_src_split source split feature status
* @has_cdp Client driver prefetch feature status
+ * @dma_formats Supported formats for dma pipe
+ * @cursor_formats Supported formats for cursor pipe
+ * @vig_formats Supported formats for vig pipe
+ * @wb_formats Supported formats for wb
*/
struct sde_mdss_cfg {
u32 hwversion;
@@ -672,6 +708,14 @@ 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_format_extended *dma_formats;
+ struct sde_format_extended *cursor_formats;
+ struct sde_format_extended *vig_formats;
+ struct sde_format_extended *wb_formats;
};
struct sde_mdss_hw_cfg_handler {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h
index 296694422653..dbc8981a7f8f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.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
@@ -16,17 +16,17 @@ static const struct sde_format_extended plane_formats[] = {
{DRM_FORMAT_ARGB8888, 0},
{DRM_FORMAT_ABGR8888, 0},
{DRM_FORMAT_RGBA8888, 0},
- {DRM_FORMAT_RGBA8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_BGRA8888, 0},
{DRM_FORMAT_XRGB8888, 0},
{DRM_FORMAT_RGBX8888, 0},
{DRM_FORMAT_BGRX8888, 0},
{DRM_FORMAT_XBGR8888, 0},
- {DRM_FORMAT_RGBX8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_RGB888, 0},
{DRM_FORMAT_BGR888, 0},
{DRM_FORMAT_RGB565, 0},
- {DRM_FORMAT_RGB565, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_BGR565, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_BGR565, 0},
{DRM_FORMAT_ARGB1555, 0},
{DRM_FORMAT_ABGR1555, 0},
@@ -52,16 +52,16 @@ static const struct sde_format_extended plane_formats_yuv[] = {
{DRM_FORMAT_ABGR8888, 0},
{DRM_FORMAT_RGBA8888, 0},
{DRM_FORMAT_BGRX8888, 0},
- {DRM_FORMAT_RGBA8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_BGRA8888, 0},
{DRM_FORMAT_XRGB8888, 0},
{DRM_FORMAT_XBGR8888, 0},
{DRM_FORMAT_RGBX8888, 0},
- {DRM_FORMAT_RGBX8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_RGB888, 0},
{DRM_FORMAT_BGR888, 0},
{DRM_FORMAT_RGB565, 0},
- {DRM_FORMAT_RGB565, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_BGR565, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_BGR565, 0},
{DRM_FORMAT_ARGB1555, 0},
{DRM_FORMAT_ABGR1555, 0},
@@ -94,13 +94,33 @@ static const struct sde_format_extended plane_formats_yuv[] = {
{0, 0},
};
+static const struct sde_format_extended cursor_formats[] = {
+ {DRM_FORMAT_ARGB8888, 0},
+ {DRM_FORMAT_ABGR8888, 0},
+ {DRM_FORMAT_RGBA8888, 0},
+ {DRM_FORMAT_BGRA8888, 0},
+ {DRM_FORMAT_XRGB8888, 0},
+ {DRM_FORMAT_ARGB1555, 0},
+ {DRM_FORMAT_ABGR1555, 0},
+ {DRM_FORMAT_RGBA5551, 0},
+ {DRM_FORMAT_BGRA5551, 0},
+ {DRM_FORMAT_ARGB4444, 0},
+ {DRM_FORMAT_ABGR4444, 0},
+ {DRM_FORMAT_RGBA4444, 0},
+ {DRM_FORMAT_BGRA4444, 0},
+ {0, 0},
+};
+
static const struct sde_format_extended wb2_formats[] = {
{DRM_FORMAT_RGB565, 0},
+ {DRM_FORMAT_BGR565, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_RGB888, 0},
{DRM_FORMAT_ARGB8888, 0},
{DRM_FORMAT_RGBA8888, 0},
+ {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_XRGB8888, 0},
{DRM_FORMAT_RGBX8888, 0},
+ {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_ARGB1555, 0},
{DRM_FORMAT_RGBA5551, 0},
{DRM_FORMAT_XRGB1555, 0},
@@ -127,8 +147,31 @@ static const struct sde_format_extended wb2_formats[] = {
{DRM_FORMAT_YUV420, 0},
{DRM_FORMAT_NV12, 0},
+ {DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_COMPRESSED},
{DRM_FORMAT_NV16, 0},
{DRM_FORMAT_YUYV, 0},
{0, 0},
};
+
+static const struct sde_format_extended rgb_10bit_formats[] = {
+ {DRM_FORMAT_BGRA1010102, 0},
+ {DRM_FORMAT_BGRX1010102, 0},
+ {DRM_FORMAT_RGBA1010102, 0},
+ {DRM_FORMAT_RGBX1010102, 0},
+ {DRM_FORMAT_ABGR2101010, 0},
+ {DRM_FORMAT_ABGR2101010, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_XBGR2101010, 0},
+ {DRM_FORMAT_XBGR2101010, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_ARGB2101010, 0},
+ {DRM_FORMAT_XRGB2101010, 0},
+};
+
+static const struct sde_format_extended p010_formats[] = {
+ {DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_DX},
+};
+
+static const struct sde_format_extended tp10_ubwc_formats[] = {
+ {DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_COMPRESSED |
+ DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_TIGHT},
+};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index c7cbb93bece4..188649d946d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.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
@@ -88,50 +88,12 @@ static struct sde_cdm_cfg *_cdm_offset(enum sde_cdm cdm,
return ERR_PTR(-EINVAL);
}
-static void sde_hw_cdm_setup_csc_10bit(struct sde_hw_cdm *ctx,
+static int sde_hw_cdm_setup_csc_10bit(struct sde_hw_cdm *ctx,
struct sde_csc_cfg *data)
{
- struct sde_hw_blk_reg_map *c = &ctx->hw;
- u32 csc_reg_off = CDM_CSC_10_MATRIX_COEFF_0;
- u32 val;
-
- /* matrix coeff */
- val = data->csc_mv[0] | (data->csc_mv[1] << 16);
- SDE_REG_WRITE(c, csc_reg_off, val);
- val = data->csc_mv[2] | (data->csc_mv[3] << 16);
- SDE_REG_WRITE(c, csc_reg_off + 0x4, val);
- val = data->csc_mv[4] | (data->csc_mv[5] << 16);
- SDE_REG_WRITE(c, csc_reg_off + 0x8, val);
- val = data->csc_mv[6] | (data->csc_mv[7] << 16);
- SDE_REG_WRITE(c, csc_reg_off + 0xc, val);
- val = data->csc_mv[8];
- SDE_REG_WRITE(c, csc_reg_off + 0x10, val);
-
- /* Pre clamp */
- val = (data->csc_pre_lv[0] << 16) | data->csc_pre_lv[1];
- SDE_REG_WRITE(c, csc_reg_off + 0x14, val);
- val = (data->csc_pre_lv[2] << 16) | data->csc_pre_lv[3];
- SDE_REG_WRITE(c, csc_reg_off + 0x18, val);
- val = (data->csc_pre_lv[4] << 16) | data->csc_pre_lv[5];
- SDE_REG_WRITE(c, csc_reg_off + 0x1c, val);
-
- /* Post clamp */
- val = (data->csc_post_lv[0] << 16) | data->csc_post_lv[1];
- SDE_REG_WRITE(c, csc_reg_off + 0x20, val);
- val = (data->csc_post_lv[2] << 16) | data->csc_post_lv[3];
- SDE_REG_WRITE(c, csc_reg_off + 0x24, val);
- val = (data->csc_post_lv[4] << 16) | data->csc_post_lv[5];
- SDE_REG_WRITE(c, csc_reg_off + 0x28, val);
-
- /* Pre-Bias */
- SDE_REG_WRITE(c, csc_reg_off + 0x2c, data->csc_pre_bv[0]);
- SDE_REG_WRITE(c, csc_reg_off + 0x30, data->csc_pre_bv[1]);
- SDE_REG_WRITE(c, csc_reg_off + 0x34, data->csc_pre_bv[2]);
-
- /* Post-Bias */
- SDE_REG_WRITE(c, csc_reg_off + 0x38, data->csc_post_bv[0]);
- SDE_REG_WRITE(c, csc_reg_off + 0x3c, data->csc_post_bv[1]);
- SDE_REG_WRITE(c, csc_reg_off + 0x40, data->csc_post_bv[2]);
+ sde_hw_csc_setup(&ctx->hw, CDM_CSC_10_MATRIX_COEFF_0, data, true);
+
+ return 0;
}
static int sde_hw_cdm_setup_cdwn(struct sde_hw_cdm *ctx,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.h b/drivers/gpu/drm/msm/sde/sde_hw_cdm.h
index 264b8a418573..a0afd897e867 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.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
@@ -65,8 +65,9 @@ struct sde_hw_cdm_ops {
* to program a different matrix than default matrix.
* @cdm: Pointer to the chroma down context structure
* @data Pointer to CSC configuration data
+ * return: 0 if success; error code otherwise
*/
- void (*setup_csc_data)(struct sde_hw_cdm *cdm,
+ int (*setup_csc_data)(struct sde_hw_cdm *cdm,
struct sde_csc_cfg *data);
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
index 49930365d989..1535d1d1ade5 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -357,37 +357,32 @@ static const struct sde_irq_type sde_irq_map[] = {
SDE_INTR_HIST_VIG_1_RSTSEQ_DONE, 2},
{ SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
{ SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
- /* irq_idx: 68-71 */
+ /* irq_idx: 72-75 */
{ SDE_IRQ_TYPE_HIST_VIG_DONE, SSPP_VIG2, SDE_INTR_HIST_VIG_2_DONE, 2},
{ SDE_IRQ_TYPE_HIST_VIG_RSTSEQ, SSPP_VIG2,
SDE_INTR_HIST_VIG_2_RSTSEQ_DONE, 2},
{ SDE_IRQ_TYPE_HIST_VIG_DONE, SSPP_VIG3, SDE_INTR_HIST_VIG_3_DONE, 2},
{ SDE_IRQ_TYPE_HIST_VIG_RSTSEQ, SSPP_VIG3,
SDE_INTR_HIST_VIG_3_RSTSEQ_DONE, 2},
- /* irq_idx: 72-75 */
+ /* irq_idx: 76-79 */
{ SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_0, SDE_INTR_HIST_DSPP_0_DONE, 2},
{ SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_0,
SDE_INTR_HIST_DSPP_0_RSTSEQ_DONE, 2},
{ SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
{ SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
- /* irq_idx: 76-79 */
+ /* irq_idx: 80-83 */
{ SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_1, SDE_INTR_HIST_DSPP_1_DONE, 2},
{ SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_1,
SDE_INTR_HIST_DSPP_1_RSTSEQ_DONE, 2},
{ SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
{ SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
- /* irq_idx: 80-83 */
+ /* irq_idx: 84-87 */
{ SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_2, SDE_INTR_HIST_DSPP_2_DONE, 2},
{ SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_2,
SDE_INTR_HIST_DSPP_2_RSTSEQ_DONE, 2},
{ SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_3, SDE_INTR_HIST_DSPP_3_DONE, 2},
{ SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_3,
SDE_INTR_HIST_DSPP_3_RSTSEQ_DONE, 2},
- /* irq_idx: 84-87 */
- { SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
- { SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
- { SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
- { SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
/* irq_idx: 88-91 */
{ SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
{ SDE_IRQ_TYPE_RESERVED, 0, 0, 2},
@@ -905,7 +900,7 @@ static u32 sde_hw_intr_get_interrupt_status(struct sde_hw_intr *intr,
sde_intr_set[reg_idx].status_off) &
sde_irq_map[irq_idx].irq_mask;
if (intr_status && clear)
- SDE_REG_WRITE(&intr->hw, sde_intr_set[irq_idx].clr_off,
+ SDE_REG_WRITE(&intr->hw, sde_intr_set[reg_idx].clr_off,
intr_status);
spin_unlock_irqrestore(&intr->mask_lock, irq_flags);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index dcba248d27b0..2592fe26cb38 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.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
@@ -41,13 +41,27 @@
#define SDE_MAX_DE_CURVES 3
#endif
-#define SDE_FORMAT_FLAG_YUV (1 << 0)
-#define SDE_FORMAT_FLAG_DX (1 << 1)
+enum sde_format_flags {
+ SDE_FORMAT_FLAG_YUV_BIT,
+ SDE_FORMAT_FLAG_DX_BIT,
+ SDE_FORMAT_FLAG_COMPRESSED_BIT,
+ SDE_FORMAT_FLAG_BIT_MAX,
+};
-#define SDE_FORMAT_IS_YUV(X) ((X)->flag & SDE_FORMAT_FLAG_YUV)
-#define SDE_FORMAT_IS_DX(X) ((X)->flag & SDE_FORMAT_FLAG_DX)
+#define SDE_FORMAT_FLAG_YUV BIT(SDE_FORMAT_FLAG_YUV_BIT)
+#define SDE_FORMAT_FLAG_DX BIT(SDE_FORMAT_FLAG_DX_BIT)
+#define SDE_FORMAT_FLAG_COMPRESSED BIT(SDE_FORMAT_FLAG_COMPRESSED_BIT)
+#define SDE_FORMAT_IS_YUV(X) \
+ (test_bit(SDE_FORMAT_FLAG_YUV_BIT, (X)->flag))
+#define SDE_FORMAT_IS_DX(X) \
+ (test_bit(SDE_FORMAT_FLAG_DX_BIT, (X)->flag))
#define SDE_FORMAT_IS_LINEAR(X) ((X)->fetch_mode == SDE_FETCH_LINEAR)
-#define SDE_FORMAT_IS_UBWC(X) ((X)->fetch_mode == SDE_FETCH_UBWC)
+#define SDE_FORMAT_IS_TILE(X) \
+ (((X)->fetch_mode == SDE_FETCH_UBWC) && \
+ !test_bit(SDE_FORMAT_FLAG_COMPRESSED_BIT, (X)->flag))
+#define SDE_FORMAT_IS_UBWC(X) \
+ (((X)->fetch_mode == SDE_FETCH_UBWC) && \
+ test_bit(SDE_FORMAT_FLAG_COMPRESSED_BIT, (X)->flag))
#define SDE_BLEND_FG_ALPHA_FG_CONST (0 << 0)
#define SDE_BLEND_FG_ALPHA_BG_CONST (1 << 0)
@@ -357,7 +371,7 @@ struct sde_format {
u8 alpha_enable;
u8 num_planes;
enum sde_fetch_type fetch_mode;
- u32 flag;
+ DECLARE_BITMAP(flag, SDE_FORMAT_FLAG_BIT_MAX);
u16 tile_width;
u16 tile_height;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 882a1c84e9a2..ea2890d776ae 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.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
@@ -755,14 +755,17 @@ static void sde_hw_sspp_setup_csc(struct sde_hw_pipe *ctx,
struct sde_csc_cfg *data)
{
u32 idx;
+ bool csc10 = false;
if (_sspp_subblk_offset(ctx, SDE_SSPP_CSC, &idx) || !data)
return;
- if (test_bit(SDE_SSPP_CSC_10BIT, &ctx->cap->features))
+ if (test_bit(SDE_SSPP_CSC_10BIT, &ctx->cap->features)) {
idx += CSC_10BIT_OFFSET;
+ csc10 = true;
+ }
- sde_hw_csc_setup(&ctx->hw, idx, data);
+ sde_hw_csc_setup(&ctx->hw, idx, data, csc10);
}
static void sde_hw_sspp_setup_sharpening(struct sde_hw_pipe *ctx,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.c b/drivers/gpu/drm/msm/sde/sde_hw_util.c
index 6f52f31a7569..b899f0c2f71c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_util.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_util.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
@@ -42,9 +42,10 @@ u32 *sde_hw_util_get_log_mask_ptr(void)
void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c,
u32 csc_reg_off,
- struct sde_csc_cfg *data)
+ struct sde_csc_cfg *data, bool csc10)
{
static const u32 matrix_shift = 7;
+ u32 clamp_shift = csc10 ? 16 : 8;
u32 val;
/* matrix coeff - convert S15.16 to S4.9 */
@@ -64,19 +65,19 @@ void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c,
SDE_REG_WRITE(c, csc_reg_off + 0x10, val);
/* Pre clamp */
- val = (data->csc_pre_lv[0] << 8) | data->csc_pre_lv[1];
+ val = (data->csc_pre_lv[0] << clamp_shift) | data->csc_pre_lv[1];
SDE_REG_WRITE(c, csc_reg_off + 0x14, val);
- val = (data->csc_pre_lv[2] << 8) | data->csc_pre_lv[3];
+ val = (data->csc_pre_lv[2] << clamp_shift) | data->csc_pre_lv[3];
SDE_REG_WRITE(c, csc_reg_off + 0x18, val);
- val = (data->csc_pre_lv[4] << 8) | data->csc_pre_lv[5];
+ val = (data->csc_pre_lv[4] << clamp_shift) | data->csc_pre_lv[5];
SDE_REG_WRITE(c, csc_reg_off + 0x1c, val);
/* Post clamp */
- val = (data->csc_post_lv[0] << 8) | data->csc_post_lv[1];
+ val = (data->csc_post_lv[0] << clamp_shift) | data->csc_post_lv[1];
SDE_REG_WRITE(c, csc_reg_off + 0x20, val);
- val = (data->csc_post_lv[2] << 8) | data->csc_post_lv[3];
+ val = (data->csc_post_lv[2] << clamp_shift) | data->csc_post_lv[3];
SDE_REG_WRITE(c, csc_reg_off + 0x24, val);
- val = (data->csc_post_lv[4] << 8) | data->csc_post_lv[5];
+ val = (data->csc_post_lv[4] << clamp_shift) | data->csc_post_lv[5];
SDE_REG_WRITE(c, csc_reg_off + 0x28, val);
/* Pre-Bias */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h
index a4d8be9de907..c38c22237a57 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_util.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_util.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
@@ -49,7 +49,7 @@ void *sde_hw_util_get_dir(void);
void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c,
u32 csc_reg_off,
- struct sde_csc_cfg *data);
+ struct sde_csc_cfg *data, bool csc10);
#endif /* _SDE_HW_UTIL_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 709c9970b357..031493aa42b8 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -601,6 +601,8 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
.mode_valid = sde_hdmi_mode_valid,
.get_info = sde_hdmi_get_info,
.set_property = sde_hdmi_set_property,
+ .get_property = sde_hdmi_get_property,
+ .pre_kickoff = sde_hdmi_pre_kickoff,
};
struct msm_display_info info = {0};
struct drm_encoder *encoder;
@@ -797,6 +799,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 +841,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/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h
new file mode 100644
index 000000000000..49cca9399cb0
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_hdcp.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SDE_HDCP_H__
+#define __SDE_HDCP_H__
+
+#include <soc/qcom/scm.h>
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include "hdmi.h"
+#include "sde_kms.h"
+#include "sde_hdmi_util.h"
+
+#ifdef SDE_HDCP_DEBUG_ENABLE
+#define SDE_HDCP_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
+#else
+#define SDE_HDCP_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
+#endif
+
+enum sde_hdcp_client_id {
+ HDCP_CLIENT_HDMI,
+ HDCP_CLIENT_DP,
+};
+
+enum sde_hdcp_states {
+ HDCP_STATE_INACTIVE,
+ HDCP_STATE_AUTHENTICATING,
+ HDCP_STATE_AUTHENTICATED,
+ HDCP_STATE_AUTH_FAIL,
+ HDCP_STATE_AUTH_ENC_NONE,
+ HDCP_STATE_AUTH_ENC_1X,
+ HDCP_STATE_AUTH_ENC_2P2
+};
+
+struct sde_hdcp_init_data {
+ struct dss_io_data *core_io;
+ struct dss_io_data *qfprom_io;
+ struct dss_io_data *hdcp_io;
+ struct mutex *mutex;
+ struct workqueue_struct *workq;
+ void *cb_data;
+ void (*notify_status)(void *cb_data, enum sde_hdcp_states status);
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ u8 sink_rx_status;
+ u16 *version;
+ u32 phy_addr;
+ u32 hdmi_tx_ver;
+ bool sec_access;
+ enum sde_hdcp_client_id client_id;
+};
+
+struct sde_hdcp_ops {
+ int (*isr)(void *ptr);
+ int (*cp_irq)(void *ptr);
+ int (*reauthenticate)(void *input);
+ int (*authenticate)(void *hdcp_ctrl);
+ bool (*feature_supported)(void *input);
+ void (*off)(void *hdcp_ctrl);
+};
+
+void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data);
+void sde_hdcp_1x_deinit(void *input);
+struct sde_hdcp_ops *sde_hdcp_1x_start(void *input);
+void *sde_hdmi_hdcp2p2_init(struct sde_hdcp_init_data *init_data);
+void sde_hdmi_hdcp2p2_deinit(void *input);
+const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state);
+struct sde_hdcp_ops *sde_hdmi_hdcp2p2_start(void *input);
+#endif /* __SDE_HDCP_H__ */
diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c
new file mode 100644
index 000000000000..3aba9e307732
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c
@@ -0,0 +1,1722 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/iopoll.h>
+#include <linux/hdcp_qseecom.h>
+#include "sde_hdcp.h"
+#include "sde_hdmi_util.h"
+#include "video/msm_hdmi_hdcp_mgr.h"
+
+#define SDE_HDCP_STATE_NAME (sde_hdcp_state_name(hdcp->hdcp_state))
+
+/* HDCP Keys state based on HDMI_HDCP_LINK0_STATUS:KEYS_STATE */
+#define HDCP_KEYS_STATE_NO_KEYS 0
+#define HDCP_KEYS_STATE_NOT_CHECKED 1
+#define HDCP_KEYS_STATE_CHECKING 2
+#define HDCP_KEYS_STATE_VALID 3
+#define HDCP_KEYS_STATE_AKSV_NOT_VALID 4
+#define HDCP_KEYS_STATE_CHKSUM_MISMATCH 5
+#define HDCP_KEYS_STATE_PROD_AKSV 6
+#define HDCP_KEYS_STATE_RESERVED 7
+
+#define TZ_HDCP_CMD_ID 0x00004401
+
+#define HDCP_INT_CLR (isr->auth_success_ack | isr->auth_fail_ack | \
+ isr->auth_fail_info_ack | isr->tx_req_ack | \
+ isr->encryption_ready_ack | \
+ isr->encryption_not_ready_ack | isr->tx_req_done_ack)
+
+#define HDCP_INT_EN (isr->auth_success_mask | isr->auth_fail_mask | \
+ isr->encryption_ready_mask | \
+ isr->encryption_not_ready_mask)
+
+#define HDCP_POLL_SLEEP_US (20 * 1000)
+#define HDCP_POLL_TIMEOUT_US (HDCP_POLL_SLEEP_US * 100)
+
+#define sde_hdcp_1x_state(x) (hdcp->hdcp_state == x)
+
+struct sde_hdcp_sink_addr {
+ char *name;
+ u32 addr;
+ u32 len;
+};
+
+struct sde_hdcp_1x_reg_data {
+ u32 reg_id;
+ struct sde_hdcp_sink_addr *sink;
+};
+
+struct sde_hdcp_skaddr_map {
+ /* addresses to read from sink */
+ struct sde_hdcp_sink_addr bcaps;
+ struct sde_hdcp_sink_addr bksv;
+ struct sde_hdcp_sink_addr r0;
+ struct sde_hdcp_sink_addr bstatus;
+ struct sde_hdcp_sink_addr cp_irq_status;
+ struct sde_hdcp_sink_addr ksv_fifo;
+ struct sde_hdcp_sink_addr v_h0;
+ struct sde_hdcp_sink_addr v_h1;
+ struct sde_hdcp_sink_addr v_h2;
+ struct sde_hdcp_sink_addr v_h3;
+ struct sde_hdcp_sink_addr v_h4;
+
+ /* addresses to write to sink */
+ struct sde_hdcp_sink_addr an;
+ struct sde_hdcp_sink_addr aksv;
+ struct sde_hdcp_sink_addr ainfo;
+};
+
+struct sde_hdcp_int_set {
+ /* interrupt register */
+ u32 int_reg;
+
+ /* interrupt enable/disable masks */
+ u32 auth_success_mask;
+ u32 auth_fail_mask;
+ u32 encryption_ready_mask;
+ u32 encryption_not_ready_mask;
+ u32 tx_req_mask;
+ u32 tx_req_done_mask;
+
+ /* interrupt acknowledgment */
+ u32 auth_success_ack;
+ u32 auth_fail_ack;
+ u32 auth_fail_info_ack;
+ u32 encryption_ready_ack;
+ u32 encryption_not_ready_ack;
+ u32 tx_req_ack;
+ u32 tx_req_done_ack;
+
+ /* interrupt status */
+ u32 auth_success_int;
+ u32 auth_fail_int;
+ u32 encryption_ready;
+ u32 encryption_not_ready;
+ u32 tx_req_int;
+ u32 tx_req_done_int;
+};
+
+struct sde_hdcp_reg_set {
+ u32 status;
+ u32 keys_offset;
+ u32 r0_offset;
+ u32 v_offset;
+ u32 ctrl;
+ u32 aksv_lsb;
+ u32 aksv_msb;
+ u32 entropy_ctrl0;
+ u32 entropy_ctrl1;
+ u32 sec_sha_ctrl;
+ u32 sec_sha_data;
+ u32 sha_status;
+
+ u32 data2_0;
+ u32 data3;
+ u32 data4;
+ u32 data5;
+ u32 data6;
+
+ u32 sec_data0;
+ u32 sec_data1;
+ u32 sec_data7;
+ u32 sec_data8;
+ u32 sec_data9;
+ u32 sec_data10;
+ u32 sec_data11;
+ u32 sec_data12;
+
+ u32 reset;
+ u32 reset_bit;
+
+ u32 repeater;
+};
+
+#define HDCP_REG_SET_CLIENT_HDMI \
+ {HDMI_HDCP_LINK0_STATUS, 28, 24, 20, HDMI_HDCP_CTRL, \
+ HDMI_HDCP_SW_LOWER_AKSV, HDMI_HDCP_SW_UPPER_AKSV, \
+ HDMI_HDCP_ENTROPY_CTRL0, HDMI_HDCP_ENTROPY_CTRL1, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_CTRL, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_DATA, \
+ HDMI_HDCP_SHA_STATUS, HDMI_HDCP_RCVPORT_DATA2_0, \
+ HDMI_HDCP_RCVPORT_DATA3, HDMI_HDCP_RCVPORT_DATA4, \
+ HDMI_HDCP_RCVPORT_DATA5, HDMI_HDCP_RCVPORT_DATA6, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA0, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA1, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA7, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA8, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \
+ HDMI_HDCP_RESET, BIT(0), BIT(6)}
+
+/* To do for DP */
+#define HDCP_REG_SET_CLIENT_DP \
+ {0}
+
+#define HDCP_HDMI_SINK_ADDR_MAP \
+ {{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \
+ {"bstatus", 0x41, 2}, {"??", 0x0, 0}, {"ksv-fifo", 0x43, 0}, \
+ {"v_h0", 0x20, 4}, {"v_h1", 0x24, 4}, {"v_h2", 0x28, 4}, \
+ {"v_h3", 0x2c, 4}, {"v_h4", 0x30, 4}, {"an", 0x18, 8}, \
+ {"aksv", 0x10, 5}, {"ainfo", 0x00, 0},}
+
+#define HDCP_DP_SINK_ADDR_MAP \
+ {{"bcaps", 0x68028, 1}, {"bksv", 0x68000, 5}, {"r0'", 0x68005, 2}, \
+ {"binfo", 0x6802A, 2}, {"cp_irq_status", 0x68029, 1}, \
+ {"ksv-fifo", 0x6802C, 0}, {"v_h0", 0x68014, 4}, {"v_h1", 0x68018, 4}, \
+ {"v_h2", 0x6801C, 4}, {"v_h3", 0x68020, 4}, {"v_h4", 0x68024, 4}, \
+ {"an", 0x6800C, 8}, {"aksv", 0x68007, 5}, {"ainfo", 0x6803B, 1} }
+
+#define HDCP_HDMI_INT_SET \
+ {HDMI_HDCP_INT_CTRL, \
+ BIT(2), BIT(6), 0, 0, 0, 0, \
+ BIT(1), BIT(5), BIT(7), 0, 0, 0, 0, \
+ BIT(0), BIT(4), 0, 0, 0, 0}
+
+#define HDCP_DP_INT_SET \
+ {DP_INTR_STATUS2, \
+ BIT(17), BIT(20), BIT(24), BIT(27), 0, 0, \
+ BIT(16), BIT(19), BIT(21), BIT(23), BIT(26), 0, 0, \
+ BIT(15), BIT(18), BIT(22), BIT(25), 0, 0}
+
+struct sde_hdcp_1x {
+ u8 bcaps;
+ u32 tp_msgid;
+ u32 an_0, an_1, aksv_0, aksv_1;
+ bool sink_r0_ready;
+ bool reauth;
+ bool ksv_ready;
+ enum sde_hdcp_states hdcp_state;
+ struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
+ struct HDCP_V2V1_MSG_TOPOLOGY current_tp;
+ struct delayed_work hdcp_auth_work;
+ struct completion r0_checked;
+ struct completion sink_r0_available;
+ struct sde_hdcp_init_data init_data;
+ struct sde_hdcp_ops *ops;
+ struct sde_hdcp_reg_set reg_set;
+ struct sde_hdcp_int_set int_set;
+ struct sde_hdcp_skaddr_map sink_addr;
+ struct workqueue_struct *workq;
+};
+
+const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state)
+{
+ switch (hdcp_state) {
+ case HDCP_STATE_INACTIVE: return "HDCP_STATE_INACTIVE";
+ case HDCP_STATE_AUTHENTICATING: return "HDCP_STATE_AUTHENTICATING";
+ case HDCP_STATE_AUTHENTICATED: return "HDCP_STATE_AUTHENTICATED";
+ case HDCP_STATE_AUTH_FAIL: return "HDCP_STATE_AUTH_FAIL";
+ default: return "???";
+ }
+}
+
+static int sde_hdcp_1x_count_one(u8 *array, u8 len)
+{
+ int i, j, count = 0;
+
+ for (i = 0; i < len; i++)
+ for (j = 0; j < 8; j++)
+ count += (((array[i] >> j) & 0x1) ? 1 : 0);
+ return count;
+}
+
+static void reset_hdcp_ddc_failures(struct sde_hdcp_1x *hdcp)
+{
+ int hdcp_ddc_ctrl1_reg;
+ int hdcp_ddc_status;
+ int failure;
+ int nack0;
+ struct dss_io_data *io;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = hdcp->init_data.core_io;
+
+ /* Check for any DDC transfer failures */
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+ failure = (hdcp_ddc_status >> 16) & BIT(0);
+ nack0 = (hdcp_ddc_status >> 14) & BIT(0);
+ SDE_HDCP_DEBUG("%s: HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d\n",
+ SDE_HDCP_STATE_NAME, hdcp_ddc_status, failure, nack0);
+
+ if (failure) {
+ /*
+ * Indicates that the last HDCP HW DDC transfer failed.
+ * This occurs when a transfer is attempted with HDCP DDC
+ * disabled (HDCP_DDC_DISABLE=1) or the number of retries
+ * matches HDCP_DDC_RETRY_CNT.
+ * Failure occurred, let's clear it.
+ */
+ SDE_HDCP_DEBUG("%s: DDC failure HDCP_DDC_STATUS=0x%08x\n",
+ SDE_HDCP_STATE_NAME, hdcp_ddc_status);
+
+ /* First, Disable DDC */
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_0, BIT(0));
+
+ /* ACK the Failure to Clear it */
+ hdcp_ddc_ctrl1_reg = DSS_REG_R(io, HDMI_HDCP_DDC_CTRL_1);
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_1,
+ hdcp_ddc_ctrl1_reg | BIT(0));
+
+ /* Check if the FAILURE got Cleared */
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+ hdcp_ddc_status = (hdcp_ddc_status >> 16) & BIT(0);
+ if (hdcp_ddc_status == 0x0)
+ SDE_HDCP_DEBUG("%s: HDCP DDC Failure cleared\n",
+ SDE_HDCP_STATE_NAME);
+ else
+ SDE_ERROR("%s: Unable to clear HDCP DDC Failure",
+ SDE_HDCP_STATE_NAME);
+
+ /* Re-Enable HDCP DDC */
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_0, 0);
+ }
+
+ if (nack0) {
+ SDE_HDCP_DEBUG("%s: Before: HDMI_DDC_SW_STATUS=0x%08x\n",
+ SDE_HDCP_STATE_NAME, DSS_REG_R(io, HDMI_DDC_SW_STATUS));
+ /* Reset HDMI DDC software status */
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) | BIT(3));
+ msleep(20);
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) & ~(BIT(3)));
+
+ /* Reset HDMI DDC Controller */
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) | BIT(1));
+ msleep(20);
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) & ~BIT(1));
+ SDE_HDCP_DEBUG("%s: After: HDMI_DDC_SW_STATUS=0x%08x\n",
+ SDE_HDCP_STATE_NAME, DSS_REG_R(io, HDMI_DDC_SW_STATUS));
+ }
+
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+
+ failure = (hdcp_ddc_status >> 16) & BIT(0);
+ nack0 = (hdcp_ddc_status >> 14) & BIT(0);
+ SDE_HDCP_DEBUG("%s: On Exit: HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d\n",
+ SDE_HDCP_STATE_NAME, hdcp_ddc_status, failure, nack0);
+} /* reset_hdcp_ddc_failures */
+
+static void sde_hdcp_1x_hw_ddc_clean(struct sde_hdcp_1x *hdcp)
+{
+ struct dss_io_data *io = NULL;
+ u32 hdcp_ddc_status, ddc_hw_status;
+ u32 ddc_xfer_done, ddc_xfer_req;
+ u32 ddc_hw_req, ddc_hw_not_idle;
+ bool ddc_hw_not_ready, xfer_not_done, hw_not_done;
+ u32 timeout_count;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = hdcp->init_data.core_io;
+ if (!io->base) {
+ pr_err("core io not inititalized\n");
+ return;
+ }
+
+ /* Wait to be clean on DDC HW engine */
+ timeout_count = 100;
+ do {
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+ ddc_xfer_req = hdcp_ddc_status & BIT(4);
+ ddc_xfer_done = hdcp_ddc_status & BIT(10);
+
+ ddc_hw_status = DSS_REG_R(io, HDMI_DDC_HW_STATUS);
+ ddc_hw_req = ddc_hw_status & BIT(16);
+ ddc_hw_not_idle = ddc_hw_status & (BIT(0) | BIT(1));
+
+ /* ddc transfer was requested but not completed */
+ xfer_not_done = ddc_xfer_req && !ddc_xfer_done;
+
+ /* ddc status is not idle or a hw request pending */
+ hw_not_done = ddc_hw_not_idle || ddc_hw_req;
+
+ ddc_hw_not_ready = xfer_not_done || hw_not_done;
+
+ SDE_HDCP_DEBUG("%s: timeout count(%d): ddc hw%sready\n",
+ SDE_HDCP_STATE_NAME, timeout_count,
+ ddc_hw_not_ready ? " not " : " ");
+ SDE_HDCP_DEBUG("hdcp_ddc_status[0x%x], ddc_hw_status[0x%x]\n",
+ hdcp_ddc_status, ddc_hw_status);
+ if (ddc_hw_not_ready)
+ msleep(20);
+ } while (ddc_hw_not_ready && --timeout_count);
+} /* hdcp_1x_hw_ddc_clean */
+
+static int sde_hdcp_1x_load_keys(void *input)
+{
+ int rc = 0;
+ bool use_sw_keys = false;
+ u32 reg_val;
+ u32 ksv_lsb_addr, ksv_msb_addr;
+ u32 aksv_lsb, aksv_msb;
+ u8 aksv[5];
+ struct dss_io_data *io;
+ struct dss_io_data *qfprom_io;
+ struct sde_hdcp_1x *hdcp = input;
+ struct sde_hdcp_reg_set *reg_set;
+
+ if (!hdcp || !hdcp->init_data.core_io ||
+ !hdcp->init_data.qfprom_io) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE) &&
+ !sde_hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) {
+ pr_err("%s: invalid state. returning\n",
+ SDE_HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ io = hdcp->init_data.core_io;
+ qfprom_io = hdcp->init_data.qfprom_io;
+ reg_set = &hdcp->reg_set;
+
+ /* On compatible hardware, use SW keys */
+ reg_val = DSS_REG_R(qfprom_io, SEC_CTRL_HW_VERSION);
+ if (reg_val >= HDCP_SEL_MIN_SEC_VERSION) {
+ reg_val = DSS_REG_R(qfprom_io,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_MSB +
+ QFPROM_RAW_VERSION_4);
+
+ if (!(reg_val & BIT(23)))
+ use_sw_keys = true;
+ }
+
+ if (use_sw_keys) {
+ if (hdcp1_set_keys(&aksv_msb, &aksv_lsb)) {
+ pr_err("setting hdcp SW keys failed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+ } else {
+ /* Fetch aksv from QFPROM, this info should be public. */
+ ksv_lsb_addr = HDCP_KSV_LSB;
+ ksv_msb_addr = HDCP_KSV_MSB;
+
+ if (hdcp->init_data.sec_access) {
+ ksv_lsb_addr += HDCP_KSV_VERSION_4_OFFSET;
+ ksv_msb_addr += HDCP_KSV_VERSION_4_OFFSET;
+ }
+
+ aksv_lsb = DSS_REG_R(qfprom_io, ksv_lsb_addr);
+ aksv_msb = DSS_REG_R(qfprom_io, ksv_msb_addr);
+ }
+
+ SDE_HDCP_DEBUG("%s: AKSV=%02x%08x\n", SDE_HDCP_STATE_NAME,
+ aksv_msb, aksv_lsb);
+
+ aksv[0] = aksv_lsb & 0xFF;
+ aksv[1] = (aksv_lsb >> 8) & 0xFF;
+ aksv[2] = (aksv_lsb >> 16) & 0xFF;
+ aksv[3] = (aksv_lsb >> 24) & 0xFF;
+ aksv[4] = aksv_msb & 0xFF;
+
+ /* check there are 20 ones in AKSV */
+ if (sde_hdcp_1x_count_one(aksv, 5) != 20) {
+ pr_err("AKSV bit count failed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ DSS_REG_W(io, reg_set->aksv_lsb, aksv_lsb);
+ DSS_REG_W(io, reg_set->aksv_msb, aksv_msb);
+
+ /* Setup seed values for random number An */
+ DSS_REG_W(io, reg_set->entropy_ctrl0, 0xB1FFB0FF);
+ DSS_REG_W(io, reg_set->entropy_ctrl1, 0xF00DFACE);
+
+ /* make sure hw is programmed */
+ wmb();
+
+ /* enable hdcp engine */
+ DSS_REG_W(io, reg_set->ctrl, 0x1);
+
+ hdcp->hdcp_state = HDCP_STATE_AUTHENTICATING;
+end:
+ return rc;
+}
+
+static int sde_hdcp_1x_read(struct sde_hdcp_1x *hdcp,
+ struct sde_hdcp_sink_addr *sink,
+ u8 *buf, bool realign)
+{
+ u32 rc = 0;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ reset_hdcp_ddc_failures(hdcp);
+
+ ddc_ctrl = hdcp->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = 0x74;
+ ddc_data->offset = sink->addr;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = sink->len;
+ ddc_data->request_len = sink->len;
+ ddc_data->retry = 5;
+ ddc_data->what = sink->name;
+ ddc_data->retry_align = realign;
+
+ rc = sde_hdmi_ddc_read((void *)hdcp->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("%s: %s read failed\n",
+ SDE_HDCP_STATE_NAME, sink->name);
+ } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ /* To-do DP APIs go here */
+ }
+
+ return rc;
+}
+
+static int sde_hdcp_1x_write(struct sde_hdcp_1x *hdcp,
+ struct sde_hdcp_sink_addr *sink, u8 *buf)
+{
+ int rc = 0;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ ddc_ctrl = hdcp->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+ memset(ddc_data, 0, sizeof(*ddc_data));
+
+ ddc_data->dev_addr = 0x74;
+ ddc_data->offset = sink->addr;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = sink->len;
+ ddc_data->what = sink->name;
+
+ rc = sde_hdmi_ddc_write((void *)hdcp->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("%s: %s write failed\n",
+ SDE_HDCP_STATE_NAME, sink->name);
+ } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ /* To-do DP APIs go here */
+ }
+
+ return rc;
+}
+
+static void sde_hdcp_1x_enable_interrupts(struct sde_hdcp_1x *hdcp)
+{
+ u32 intr_reg;
+ struct dss_io_data *io;
+ struct sde_hdcp_int_set *isr;
+
+ io = hdcp->init_data.core_io;
+ isr = &hdcp->int_set;
+
+ intr_reg = DSS_REG_R(io, isr->int_reg);
+
+ intr_reg |= HDCP_INT_CLR | HDCP_INT_EN;
+
+ DSS_REG_W(io, isr->int_reg, intr_reg);
+}
+
+static int sde_hdcp_1x_read_bcaps(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct dss_io_data *hdcp_io = hdcp->init_data.hdcp_io;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bcaps,
+ &hdcp->bcaps, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading bcaps\n");
+ goto error;
+ }
+
+ SDE_HDCP_DEBUG("bcaps read: 0x%x\n", hdcp->bcaps);
+
+ hdcp->current_tp.ds_type = hdcp->bcaps & reg_set->repeater ?
+ DS_REPEATER : DS_RECEIVER;
+
+ SDE_HDCP_DEBUG("ds: %s\n", hdcp->current_tp.ds_type == DS_REPEATER ?
+ "repeater" : "receiver");
+
+ /* Write BCAPS to the hardware */
+ DSS_REG_W(hdcp_io, reg_set->sec_data12, hdcp->bcaps);
+error:
+ return rc;
+}
+
+static int sde_hdcp_1x_wait_for_hw_ready(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u32 link0_status;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct dss_io_data *io = hdcp->init_data.core_io;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ /* Wait for HDCP keys to be checked and validated */
+ rc = readl_poll_timeout(io->base + reg_set->status, link0_status,
+ ((link0_status >> reg_set->keys_offset) & 0x7)
+ == HDCP_KEYS_STATE_VALID ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("key not ready\n");
+ goto error;
+ }
+
+ /*
+ * 1.1_Features turned off by default.
+ * No need to write AInfo since 1.1_Features is disabled.
+ */
+ DSS_REG_W(io, reg_set->data4, 0);
+
+ /* Wait for An0 and An1 bit to be ready */
+ rc = readl_poll_timeout(io->base + reg_set->status, link0_status,
+ (link0_status & (BIT(8) | BIT(9))) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("An not ready\n");
+ goto error;
+ }
+
+ /* As per hardware recommendations, wait before reading An */
+ msleep(20);
+error:
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int sde_hdcp_1x_send_an_aksv_to_sink(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u8 an[8], aksv[5];
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ an[0] = hdcp->an_0 & 0xFF;
+ an[1] = (hdcp->an_0 >> 8) & 0xFF;
+ an[2] = (hdcp->an_0 >> 16) & 0xFF;
+ an[3] = (hdcp->an_0 >> 24) & 0xFF;
+ an[4] = hdcp->an_1 & 0xFF;
+ an[5] = (hdcp->an_1 >> 8) & 0xFF;
+ an[6] = (hdcp->an_1 >> 16) & 0xFF;
+ an[7] = (hdcp->an_1 >> 24) & 0xFF;
+
+ SDE_HDCP_DEBUG("an read: 0x%2x%2x%2x%2x%2x%2x%2x%2x\n",
+ an[7], an[6], an[5], an[4], an[3], an[2], an[1], an[0]);
+
+ rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.an, an);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error writing an to sink\n");
+ goto error;
+ }
+
+ /* Copy An and AKSV to byte arrays for transmission */
+ aksv[0] = hdcp->aksv_0 & 0xFF;
+ aksv[1] = (hdcp->aksv_0 >> 8) & 0xFF;
+ aksv[2] = (hdcp->aksv_0 >> 16) & 0xFF;
+ aksv[3] = (hdcp->aksv_0 >> 24) & 0xFF;
+ aksv[4] = hdcp->aksv_1 & 0xFF;
+
+ SDE_HDCP_DEBUG("aksv read: 0x%2x%2x%2x%2x%2x\n",
+ aksv[4], aksv[3], aksv[2], aksv[1], aksv[0]);
+
+ rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.aksv, aksv);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error writing aksv to sink\n");
+ goto error;
+ }
+error:
+ return rc;
+}
+
+static int sde_hdcp_1x_read_an_aksv_from_hw(struct sde_hdcp_1x *hdcp)
+{
+ struct dss_io_data *io = hdcp->init_data.core_io;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ hdcp->an_0 = DSS_REG_R(io, reg_set->data5);
+ if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ udelay(1);
+ hdcp->an_0 = DSS_REG_R(io, reg_set->data5);
+ }
+
+ hdcp->an_1 = DSS_REG_R(io, reg_set->data6);
+ if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ udelay(1);
+ hdcp->an_1 = DSS_REG_R(io, reg_set->data6);
+ }
+
+ /* Read AKSV */
+ hdcp->aksv_0 = DSS_REG_R(io, reg_set->data3);
+ hdcp->aksv_1 = DSS_REG_R(io, reg_set->data4);
+
+ return 0;
+}
+
+static int sde_hdcp_1x_get_bksv_from_sink(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u8 *bksv = hdcp->current_tp.bksv;
+ u32 link0_bksv_0, link0_bksv_1;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct dss_io_data *hdcp_io = hdcp->init_data.hdcp_io;
+
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bksv, bksv, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading bksv from sink\n");
+ goto error;
+ }
+
+ SDE_HDCP_DEBUG("bksv read: 0x%2x%2x%2x%2x%2x\n",
+ bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]);
+
+ /* check there are 20 ones in BKSV */
+ if (sde_hdcp_1x_count_one(bksv, 5) != 20) {
+ pr_err("%s: BKSV doesn't have 20 1's and 20 0's\n",
+ SDE_HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ link0_bksv_0 = bksv[3];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[2];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[1];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[0];
+ link0_bksv_1 = bksv[4];
+
+ DSS_REG_W(hdcp_io, reg_set->sec_data0, link0_bksv_0);
+ DSS_REG_W(hdcp_io, reg_set->sec_data1, link0_bksv_1);
+error:
+ return rc;
+}
+
+static void sde_hdcp_1x_enable_sink_irq_hpd(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u8 enable_hpd_irq = 0x1;
+
+ if (hdcp->current_tp.ds_type != DS_REPEATER)
+ return;
+
+ rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.ainfo, &enable_hpd_irq);
+ if (IS_ERR_VALUE(rc))
+ SDE_HDCP_DEBUG("error writing ainfo to sink\n");
+}
+
+static int sde_hdcp_1x_verify_r0(struct sde_hdcp_1x *hdcp)
+{
+ int rc, r0_retry = 3;
+ u8 buf[2];
+ u32 link0_status, timeout_count;
+ u32 const r0_read_delay_us = 1;
+ u32 const r0_read_timeout_us = r0_read_delay_us * 10;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct dss_io_data *io = hdcp->init_data.core_io;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ /* Wait for HDCP R0 computation to be completed */
+ rc = readl_poll_timeout(io->base + reg_set->status, link0_status,
+ (link0_status & BIT(reg_set->r0_offset)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("R0 not ready\n");
+ goto error;
+ }
+
+ /*
+ * HDCP Compliace Test case 1A-01:
+ * Wait here at least 100ms before reading R0'
+ */
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ msleep(100);
+ } else {
+ if (!hdcp->sink_r0_ready) {
+ reinit_completion(&hdcp->sink_r0_available);
+ timeout_count = wait_for_completion_timeout(
+ &hdcp->sink_r0_available, HZ / 2);
+
+ if (hdcp->reauth) {
+ pr_err("sink R0 not ready\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ }
+ }
+
+ do {
+ memset(buf, 0, sizeof(buf));
+
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.r0,
+ buf, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading R0' from sink\n");
+ goto error;
+ }
+
+ SDE_HDCP_DEBUG("sink R0'read: %2x%2x\n", buf[1], buf[0]);
+
+ DSS_REG_W(io, reg_set->data2_0, (((u32)buf[1]) << 8) | buf[0]);
+
+ rc = readl_poll_timeout(io->base + reg_set->status,
+ link0_status, (link0_status & BIT(12)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ r0_read_delay_us, r0_read_timeout_us);
+ } while (rc && --r0_retry);
+error:
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int sde_hdcp_1x_authentication_part1(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ sde_hdcp_1x_enable_interrupts(hdcp);
+
+ rc = sde_hdcp_1x_read_bcaps(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_wait_for_hw_ready(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_read_an_aksv_from_hw(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_get_bksv_from_sink(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_send_an_aksv_to_sink(hdcp);
+ if (rc)
+ goto error;
+
+ sde_hdcp_1x_enable_sink_irq_hpd(hdcp);
+
+ rc = sde_hdcp_1x_verify_r0(hdcp);
+ if (rc)
+ goto error;
+
+ pr_info("SUCCESSFUL\n");
+
+ return 0;
+error:
+ pr_err("%s: FAILED\n", SDE_HDCP_STATE_NAME);
+
+ return rc;
+}
+
+static int sde_hdcp_1x_transfer_v_h(struct sde_hdcp_1x *hdcp)
+{
+ int rc = 0;
+ struct dss_io_data *io = hdcp->init_data.hdcp_io;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct sde_hdcp_1x_reg_data reg_data[] = {
+ {reg_set->sec_data7, &hdcp->sink_addr.v_h0},
+ {reg_set->sec_data8, &hdcp->sink_addr.v_h1},
+ {reg_set->sec_data9, &hdcp->sink_addr.v_h2},
+ {reg_set->sec_data10, &hdcp->sink_addr.v_h3},
+ {reg_set->sec_data11, &hdcp->sink_addr.v_h4},
+ };
+ struct sde_hdcp_sink_addr sink = {"V", reg_data->sink->addr};
+ u32 size = ARRAY_SIZE(reg_data);
+ u8 buf[0xFF] = {0};
+ u32 i = 0, len = 0;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i++) {
+ struct sde_hdcp_1x_reg_data *rd = reg_data + i;
+
+ len += rd->sink->len;
+ }
+
+ sink.len = len;
+
+ rc = sde_hdcp_1x_read(hdcp, &sink, buf, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading %s\n", sink.name);
+ goto end;
+ }
+
+
+ for (i = 0; i < size; i++) {
+ struct sde_hdcp_1x_reg_data *rd = reg_data + i;
+ u32 reg_data;
+
+ memcpy(&reg_data, buf + (sizeof(u32) * i), sizeof(u32));
+ DSS_REG_W(io, rd->reg_id, reg_data);
+ }
+end:
+ return rc;
+}
+
+static int sde_hdcp_1x_validate_downstream(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u8 buf[2] = {0, 0};
+ u8 device_count, depth;
+ u8 max_cascade_exceeded, max_devs_exceeded;
+ u16 bstatus;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bstatus,
+ buf, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading bstatus\n");
+ goto end;
+ }
+
+ bstatus = buf[1];
+ bstatus = (bstatus << 8) | buf[0];
+
+ device_count = bstatus & 0x7F;
+
+ SDE_HDCP_DEBUG("device count %d\n", device_count);
+
+ /* Cascaded repeater depth */
+ depth = (bstatus >> 8) & 0x7;
+ SDE_HDCP_DEBUG("depth %d\n", depth);
+
+ /*
+ * HDCP Compliance 1B-05:
+ * Check if no. of devices connected to repeater
+ * exceed max_devices_connected from bit 7 of Bstatus.
+ */
+ max_devs_exceeded = (bstatus & BIT(7)) >> 7;
+ if (max_devs_exceeded == 0x01) {
+ pr_err("no. of devs connected exceed max allowed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /*
+ * HDCP Compliance 1B-06:
+ * Check if no. of cascade connected to repeater
+ * exceed max_cascade_connected from bit 11 of Bstatus.
+ */
+ max_cascade_exceeded = (bstatus & BIT(11)) >> 11;
+ if (max_cascade_exceeded == 0x01) {
+ pr_err("no. of cascade connections exceed max allowed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* Update topology information */
+ hdcp->current_tp.dev_count = device_count;
+ hdcp->current_tp.max_cascade_exceeded = max_cascade_exceeded;
+ hdcp->current_tp.max_dev_exceeded = max_devs_exceeded;
+ hdcp->current_tp.depth = depth;
+
+ DSS_REG_W(hdcp->init_data.hdcp_io,
+ reg_set->sec_data12, hdcp->bcaps | (bstatus << 8));
+end:
+ return rc;
+}
+
+static int sde_hdcp_1x_read_ksv_fifo(struct sde_hdcp_1x *hdcp)
+{
+ u32 ksv_read_retry = 20, ksv_bytes, rc = 0;
+ u8 *ksv_fifo = hdcp->current_tp.ksv_list;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ memset(ksv_fifo, 0, sizeof(hdcp->current_tp.ksv_list));
+
+ /* each KSV is 5 bytes long */
+ ksv_bytes = 5 * hdcp->current_tp.dev_count;
+ hdcp->sink_addr.ksv_fifo.len = ksv_bytes;
+
+ while (ksv_bytes && --ksv_read_retry) {
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.ksv_fifo,
+ ksv_fifo, true);
+ if (IS_ERR_VALUE(rc))
+ pr_err("could not read ksv fifo (%d)\n",
+ ksv_read_retry);
+ else
+ break;
+ }
+
+ if (rc)
+ pr_err("error reading ksv_fifo\n");
+
+ return rc;
+}
+
+static int sde_hdcp_1x_write_ksv_fifo(struct sde_hdcp_1x *hdcp)
+{
+ int i, rc = 0;
+ u8 *ksv_fifo = hdcp->current_tp.ksv_list;
+ u32 ksv_bytes = hdcp->sink_addr.ksv_fifo.len;
+ struct dss_io_data *io = hdcp->init_data.core_io;
+ struct dss_io_data *sec_io = hdcp->init_data.hdcp_io;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ u32 sha_status = 0, status;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ /* reset SHA Controller */
+ DSS_REG_W(sec_io, reg_set->sec_sha_ctrl, 0x1);
+ DSS_REG_W(sec_io, reg_set->sec_sha_ctrl, 0x0);
+
+ for (i = 0; i < ksv_bytes - 1; i++) {
+ /* Write KSV byte and do not set DONE bit[0] */
+ DSS_REG_W_ND(sec_io, reg_set->sec_sha_data, ksv_fifo[i] << 16);
+
+ /*
+ * Once 64 bytes have been written, we need to poll for
+ * HDCP_SHA_BLOCK_DONE before writing any further
+ */
+ if (i && !((i + 1) % 64)) {
+ rc = readl_poll_timeout(io->base + reg_set->sha_status,
+ sha_status, (sha_status & BIT(0)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("block not done\n");
+ goto error;
+ }
+ }
+ }
+
+ /* Write l to DONE bit[0] */
+ DSS_REG_W_ND(sec_io, reg_set->sec_sha_data,
+ (ksv_fifo[ksv_bytes - 1] << 16) | 0x1);
+
+ /* Now wait for HDCP_SHA_COMP_DONE */
+ rc = readl_poll_timeout(io->base + reg_set->sha_status, sha_status,
+ (sha_status & BIT(4)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("V computation not done\n");
+ goto error;
+ }
+
+ /* Wait for V_MATCHES */
+ rc = readl_poll_timeout(io->base + reg_set->status, status,
+ (status & BIT(reg_set->v_offset)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("V mismatch\n");
+ rc = -EINVAL;
+ }
+error:
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int sde_hdcp_1x_wait_for_ksv_ready(struct sde_hdcp_1x *hdcp)
+{
+ int rc, timeout;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Wait until READY bit is set in BCAPS, as per HDCP specifications
+ * maximum permitted time to check for READY bit is five seconds.
+ */
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bcaps,
+ &hdcp->bcaps, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading bcaps\n");
+ goto error;
+ }
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ timeout = 50;
+
+ while (!(hdcp->bcaps & BIT(5)) && --timeout) {
+ rc = sde_hdcp_1x_read(hdcp,
+ &hdcp->sink_addr.bcaps,
+ &hdcp->bcaps, false);
+ if (IS_ERR_VALUE(rc) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("error reading bcaps\n");
+ goto error;
+ }
+ msleep(100);
+ }
+ } else {
+ u8 cp_buf = 0;
+ struct sde_hdcp_sink_addr *sink =
+ &hdcp->sink_addr.cp_irq_status;
+
+ timeout = jiffies_to_msecs(jiffies);
+
+ while (1) {
+ rc = sde_hdcp_1x_read(hdcp, sink, &cp_buf, false);
+ if (rc)
+ goto error;
+
+ if (cp_buf & BIT(0))
+ break;
+
+ /* max timeout of 5 sec as per hdcp 1.x spec */
+ if (abs(timeout - jiffies_to_msecs(jiffies)) > 5000) {
+ timeout = 0;
+ break;
+ }
+
+ if (hdcp->ksv_ready || hdcp->reauth ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ break;
+
+ /* re-read after a minimum delay */
+ msleep(20);
+ }
+ }
+
+ if (!timeout || hdcp->reauth ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("DS KSV not ready\n");
+ rc = -EINVAL;
+ } else {
+ hdcp->ksv_ready = true;
+ }
+error:
+ return rc;
+}
+
+static int sde_hdcp_1x_authentication_part2(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ int v_retry = 3;
+
+ rc = sde_hdcp_1x_validate_downstream(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_read_ksv_fifo(hdcp);
+ if (rc)
+ goto error;
+
+ do {
+ rc = sde_hdcp_1x_transfer_v_h(hdcp);
+ if (rc)
+ goto error;
+
+ /* do not proceed further if no device connected */
+ if (!hdcp->current_tp.dev_count)
+ goto error;
+
+ rc = sde_hdcp_1x_write_ksv_fifo(hdcp);
+ } while (--v_retry && rc);
+error:
+ if (rc) {
+ pr_err("%s: FAILED\n", SDE_HDCP_STATE_NAME);
+ } else {
+ hdcp->hdcp_state = HDCP_STATE_AUTHENTICATED;
+
+ pr_info("SUCCESSFUL\n");
+ }
+
+ return rc;
+}
+
+static void sde_hdcp_1x_cache_topology(struct sde_hdcp_1x *hdcp)
+{
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ memcpy((void *)&hdcp->cached_tp,
+ (void *) &hdcp->current_tp,
+ sizeof(hdcp->cached_tp));
+ hdcp1_cache_repeater_topology((void *)&hdcp->cached_tp);
+}
+
+static void sde_hdcp_1x_notify_topology(void)
+{
+ hdcp1_notify_topology();
+}
+
+static void sde_hdcp_1x_update_auth_status(struct sde_hdcp_1x *hdcp)
+{
+ if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) {
+ sde_hdcp_1x_cache_topology(hdcp);
+ sde_hdcp_1x_notify_topology();
+ }
+
+ if (hdcp->init_data.notify_status &&
+ !sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ hdcp->init_data.notify_status(
+ hdcp->init_data.cb_data,
+ hdcp->hdcp_state);
+ }
+}
+
+static void sde_hdcp_1x_auth_work(struct work_struct *work)
+{
+ int rc;
+ struct delayed_work *dw = to_delayed_work(work);
+ struct sde_hdcp_1x *hdcp = container_of(dw,
+ struct sde_hdcp_1x, hdcp_auth_work);
+ struct dss_io_data *io;
+
+ if (!hdcp) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return;
+ }
+
+ hdcp->sink_r0_ready = false;
+ hdcp->reauth = false;
+ hdcp->ksv_ready = false;
+
+ io = hdcp->init_data.core_io;
+ /* Enabling Software DDC for HDMI and REF timer for DP */
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI)
+ DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io,
+ HDMI_DDC_ARBITRATION) & ~(BIT(4)));
+ else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ /* To do for DP */
+ }
+
+ /*
+ * program hw to enable encryption as soon as
+ * authentication is successful.
+ */
+ hdcp1_set_enc(true);
+
+ rc = sde_hdcp_1x_authentication_part1(hdcp);
+ if (rc)
+ goto end;
+
+ if (hdcp->current_tp.ds_type == DS_REPEATER) {
+ rc = sde_hdcp_1x_wait_for_ksv_ready(hdcp);
+ if (rc)
+ goto end;
+ } else {
+ hdcp->hdcp_state = HDCP_STATE_AUTHENTICATED;
+ goto end;
+ }
+
+ hdcp->ksv_ready = false;
+
+ rc = sde_hdcp_1x_authentication_part2(hdcp);
+ if (rc)
+ goto end;
+
+ /*
+ * Disabling software DDC before going into part3 to make sure
+ * there is no Arbitration between software and hardware for DDC
+ */
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI)
+ DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io,
+ HDMI_DDC_ARBITRATION) | (BIT(4)));
+end:
+ if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE))
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+
+ sde_hdcp_1x_update_auth_status(hdcp);
+}
+
+static int sde_hdcp_1x_authenticate(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+
+ if (!hdcp) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ flush_delayed_work(&hdcp->hdcp_auth_work);
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ if (!sde_hdcp_1x_load_keys(input)) {
+
+ queue_delayed_work(hdcp->workq,
+ &hdcp->hdcp_auth_work, HZ/2);
+ } else {
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+ sde_hdcp_1x_update_auth_status(hdcp);
+ }
+
+ return 0;
+} /* hdcp_1x_authenticate */
+
+static int sde_hdcp_1x_reauthenticate(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+ struct dss_io_data *io;
+ struct sde_hdcp_reg_set *reg_set;
+ struct sde_hdcp_int_set *isr;
+ u32 hdmi_hw_version;
+ u32 ret = 0, reg;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ io = hdcp->init_data.core_io;
+ reg_set = &hdcp->reg_set;
+ isr = &hdcp->int_set;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION);
+ if (hdmi_hw_version >= 0x30030000) {
+ DSS_REG_W(io, HDMI_CTRL_SW_RESET, BIT(1));
+ DSS_REG_W(io, HDMI_CTRL_SW_RESET, 0);
+ }
+
+ /* Wait to be clean on DDC HW engine */
+ sde_hdcp_1x_hw_ddc_clean(hdcp);
+ }
+
+ /* Disable HDCP interrupts */
+ DSS_REG_W(io, isr->int_reg, DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN);
+
+ reg = DSS_REG_R(io, reg_set->reset);
+ DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit);
+
+ /* Disable encryption and disable the HDCP block */
+ DSS_REG_W(io, reg_set->ctrl, 0);
+
+ DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit);
+
+ hdcp->hdcp_state = HDCP_STATE_INACTIVE;
+ sde_hdcp_1x_authenticate(hdcp);
+
+ return ret;
+} /* hdcp_1x_reauthenticate */
+
+static void sde_hdcp_1x_off(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+ struct dss_io_data *io;
+ struct sde_hdcp_reg_set *reg_set;
+ struct sde_hdcp_int_set *isr;
+ int rc = 0;
+ u32 reg;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = hdcp->init_data.core_io;
+ reg_set = &hdcp->reg_set;
+ isr = &hdcp->int_set;
+
+ if (sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ pr_err("invalid state\n");
+ return;
+ }
+
+ /*
+ * Disable HDCP interrupts.
+ * Also, need to set the state to inactive here so that any ongoing
+ * reauth works will know that the HDCP session has been turned off.
+ */
+ mutex_lock(hdcp->init_data.mutex);
+ DSS_REG_W(io, isr->int_reg,
+ DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN);
+ hdcp->hdcp_state = HDCP_STATE_INACTIVE;
+ mutex_unlock(hdcp->init_data.mutex);
+
+ /* complete any wait pending */
+ complete_all(&hdcp->sink_r0_available);
+ complete_all(&hdcp->r0_checked);
+ /*
+ * Cancel any pending auth/reauth attempts.
+ * If one is ongoing, this will wait for it to finish.
+ * No more reauthentiaction attempts will be scheduled since we
+ * set the currect state to inactive.
+ */
+ rc = cancel_delayed_work_sync(&hdcp->hdcp_auth_work);
+ if (rc)
+ SDE_HDCP_DEBUG("%s: Deleted hdcp auth work\n",
+ SDE_HDCP_STATE_NAME);
+
+ hdcp1_set_enc(false);
+
+ reg = DSS_REG_R(io, reg_set->reset);
+ DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit);
+
+ /* Disable encryption and disable the HDCP block */
+ DSS_REG_W(io, reg_set->ctrl, 0);
+
+ DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit);
+
+ hdcp->sink_r0_ready = false;
+
+ SDE_HDCP_DEBUG("%s: HDCP: Off\n", SDE_HDCP_STATE_NAME);
+} /* hdcp_1x_off */
+
+static int sde_hdcp_1x_isr(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+ int rc = 0;
+ struct dss_io_data *io;
+ u32 hdcp_int_val;
+ struct sde_hdcp_reg_set *reg_set;
+ struct sde_hdcp_int_set *isr;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ io = hdcp->init_data.core_io;
+ reg_set = &hdcp->reg_set;
+ isr = &hdcp->int_set;
+
+ hdcp_int_val = DSS_REG_R(io, isr->int_reg);
+
+ /* Ignore HDCP interrupts if HDCP is disabled */
+ if (sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ DSS_REG_W(io, isr->int_reg, hdcp_int_val | HDCP_INT_CLR);
+ return 0;
+ }
+
+ if (hdcp_int_val & isr->auth_success_int) {
+ /* AUTH_SUCCESS_INT */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->auth_success_ack));
+ SDE_HDCP_DEBUG("%s: AUTH SUCCESS\n", SDE_HDCP_STATE_NAME);
+
+ if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ complete_all(&hdcp->r0_checked);
+ }
+
+ if (hdcp_int_val & isr->auth_fail_int) {
+ /* AUTH_FAIL_INT */
+ u32 link_status = DSS_REG_R(io, reg_set->status);
+
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->auth_fail_ack));
+
+ SDE_HDCP_DEBUG("%s: AUTH FAIL, LINK0_STATUS=0x%08x\n",
+ SDE_HDCP_STATE_NAME, link_status);
+
+ if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) {
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+ sde_hdcp_1x_update_auth_status(hdcp);
+ } else if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ complete_all(&hdcp->r0_checked);
+ }
+
+ /* Clear AUTH_FAIL_INFO as well */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->auth_fail_info_ack));
+ }
+
+ if (hdcp_int_val & isr->tx_req_int) {
+ /* DDC_XFER_REQ_INT */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->tx_req_ack));
+ SDE_HDCP_DEBUG("%s: DDC_XFER_REQ_INT received\n",
+ SDE_HDCP_STATE_NAME);
+ }
+
+ if (hdcp_int_val & isr->tx_req_done_int) {
+ /* DDC_XFER_DONE_INT */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->tx_req_done_ack));
+ SDE_HDCP_DEBUG("%s: DDC_XFER_DONE received\n",
+ SDE_HDCP_STATE_NAME);
+ }
+
+ if (hdcp_int_val & isr->encryption_ready) {
+ /* Encryption enabled */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->encryption_ready_ack));
+ SDE_HDCP_DEBUG("%s: encryption ready received\n",
+ SDE_HDCP_STATE_NAME);
+ }
+
+ if (hdcp_int_val & isr->encryption_not_ready) {
+ /* Encryption enabled */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->encryption_not_ready_ack));
+ SDE_HDCP_DEBUG("%s: encryption not ready received\n",
+ SDE_HDCP_STATE_NAME);
+ }
+
+error:
+ return rc;
+}
+
+void sde_hdcp_1x_deinit(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+
+ if (!hdcp) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ if (hdcp->workq)
+ destroy_workqueue(hdcp->workq);
+
+ kfree(hdcp);
+} /* hdcp_1x_deinit */
+
+static void sde_hdcp_1x_update_client_reg_set(struct sde_hdcp_1x *hdcp)
+{
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ struct sde_hdcp_reg_set reg_set = HDCP_REG_SET_CLIENT_HDMI;
+ struct sde_hdcp_skaddr_map sink_addr = HDCP_HDMI_SINK_ADDR_MAP;
+ struct sde_hdcp_int_set isr = HDCP_HDMI_INT_SET;
+
+ hdcp->reg_set = reg_set;
+ hdcp->sink_addr = sink_addr;
+ hdcp->int_set = isr;
+ } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ /* TO DO for DP
+ * Will be filled later
+ */
+ }
+}
+
+static bool sde_hdcp_1x_is_cp_irq_raised(struct sde_hdcp_1x *hdcp)
+{
+ int ret;
+ u8 buf = 0;
+ struct sde_hdcp_sink_addr sink = {"irq", 0x201, 1};
+
+ ret = sde_hdcp_1x_read(hdcp, &sink, &buf, false);
+ if (IS_ERR_VALUE(ret))
+ pr_err("error reading irq_vector\n");
+
+ return buf & BIT(2) ? true : false;
+}
+
+static void sde_hdcp_1x_clear_cp_irq(struct sde_hdcp_1x *hdcp)
+{
+ int ret;
+ u8 buf = BIT(2);
+ struct sde_hdcp_sink_addr sink = {"irq", 0x201, 1};
+
+ ret = sde_hdcp_1x_write(hdcp, &sink, &buf);
+ if (IS_ERR_VALUE(ret))
+ pr_err("error clearing irq_vector\n");
+}
+
+static int sde_hdcp_1x_cp_irq(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+ u8 buf = 0;
+ int ret;
+
+ if (!hdcp) {
+ pr_err("invalid input\n");
+ goto irq_not_handled;
+ }
+
+ if (!sde_hdcp_1x_is_cp_irq_raised(hdcp)) {
+ SDE_HDCP_DEBUG("cp_irq not raised\n");
+ goto irq_not_handled;
+ }
+
+ ret = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.cp_irq_status,
+ &buf, false);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("error reading cp_irq_status\n");
+ goto irq_not_handled;
+ }
+
+ if ((buf & BIT(2)) || (buf & BIT(3))) {
+ pr_err("%s\n",
+ buf & BIT(2) ? "LINK_INTEGRITY_FAILURE" :
+ "REAUTHENTICATION_REQUEST");
+
+ hdcp->reauth = true;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE))
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+
+ complete_all(&hdcp->sink_r0_available);
+ sde_hdcp_1x_update_auth_status(hdcp);
+ } else if (buf & BIT(1)) {
+ SDE_HDCP_DEBUG("R0' AVAILABLE\n");
+ hdcp->sink_r0_ready = true;
+ complete_all(&hdcp->sink_r0_available);
+ } else if ((buf & BIT(0))) {
+ SDE_HDCP_DEBUG("KSVs READY\n");
+
+ hdcp->ksv_ready = true;
+ } else {
+ SDE_HDCP_DEBUG("spurious interrupt\n");
+ }
+
+ sde_hdcp_1x_clear_cp_irq(hdcp);
+ return 0;
+
+irq_not_handled:
+ return -EINVAL;
+}
+
+void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data)
+{
+ struct sde_hdcp_1x *hdcp = NULL;
+ char name[20];
+ static struct sde_hdcp_ops ops = {
+ .isr = sde_hdcp_1x_isr,
+ .cp_irq = sde_hdcp_1x_cp_irq,
+ .reauthenticate = sde_hdcp_1x_reauthenticate,
+ .authenticate = sde_hdcp_1x_authenticate,
+ .off = sde_hdcp_1x_off
+ };
+
+ if (!init_data || !init_data->core_io || !init_data->qfprom_io ||
+ !init_data->mutex || !init_data->notify_status ||
+ !init_data->workq || !init_data->cb_data) {
+ pr_err("invalid input\n");
+ goto error;
+ }
+
+ if (init_data->sec_access && !init_data->hdcp_io) {
+ pr_err("hdcp_io required\n");
+ goto error;
+ }
+
+ hdcp = kzalloc(sizeof(*hdcp), GFP_KERNEL);
+ if (!hdcp)
+ goto error;
+
+ hdcp->init_data = *init_data;
+ hdcp->ops = &ops;
+
+ snprintf(name, sizeof(name), "hdcp_1x_%d",
+ hdcp->init_data.client_id);
+
+ hdcp->workq = create_workqueue(name);
+ if (!hdcp->workq) {
+ pr_err("Error creating workqueue\n");
+ kfree(hdcp);
+ goto error;
+ }
+
+ sde_hdcp_1x_update_client_reg_set(hdcp);
+
+ INIT_DELAYED_WORK(&hdcp->hdcp_auth_work, sde_hdcp_1x_auth_work);
+
+ hdcp->hdcp_state = HDCP_STATE_INACTIVE;
+ init_completion(&hdcp->r0_checked);
+ init_completion(&hdcp->sink_r0_available);
+
+ SDE_HDCP_DEBUG("HDCP module initialized. HDCP_STATE=%s\n",
+ SDE_HDCP_STATE_NAME);
+
+ return (void *)hdcp;
+
+error:
+ return NULL;
+} /* hdcp_1x_init */
+
+struct sde_hdcp_ops *sde_hdcp_1x_start(void *input)
+{
+ return ((struct sde_hdcp_1x *)input)->ops;
+}
+
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h
index 82d3e28918fd..7e4f24ae7de8 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h
@@ -4,6 +4,7 @@
struct nvkm_alarm {
struct list_head head;
+ struct list_head exec;
u64 timestamp;
void (*func)(struct nvkm_alarm *);
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 58a3f7cf2fb3..00de1bf81519 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -370,7 +370,8 @@ nouveau_display_init(struct drm_device *dev)
return ret;
/* enable polling for external displays */
- drm_kms_helper_poll_enable(dev);
+ if (!dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_enable(dev);
/* enable hotplug interrupts */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index d236fc7c425b..91a61d2cca88 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -743,7 +743,10 @@ nouveau_pmops_runtime_resume(struct device *dev)
pci_set_master(pdev);
ret = nouveau_do_resume(drm_dev, true);
- drm_kms_helper_poll_enable(drm_dev);
+
+ if (!drm_dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_enable(drm_dev);
+
/* do magic */
nvif_mask(&device->object, 0x088488, (1 << 25), (1 << 25));
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h
index 2e3a62d38fe9..1621c8ae0fa0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.h
@@ -99,6 +99,7 @@ struct nv84_fence_priv {
struct nouveau_bo *bo;
struct nouveau_bo *bo_gart;
u32 *suspend;
+ struct mutex mutex;
};
u64 nv84_fence_crtc(struct nouveau_channel *, int);
diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c
index 6ae1b3494bcd..b7b961233949 100644
--- a/drivers/gpu/drm/nouveau/nouveau_usif.c
+++ b/drivers/gpu/drm/nouveau/nouveau_usif.c
@@ -313,7 +313,8 @@ usif_ioctl(struct drm_file *filp, void __user *user, u32 argc)
if (nvif_unpack(argv->v0, 0, 0, true)) {
/* block access to objects not created via this interface */
owner = argv->v0.owner;
- if (argv->v0.object == 0ULL)
+ if (argv->v0.object == 0ULL &&
+ argv->v0.type != NVIF_IOCTL_V0_DEL)
argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */
else
argv->v0.owner = NVDRM_OBJECT_USIF;
diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c
index 412c5be5a9ca..7bc26eceda66 100644
--- a/drivers/gpu/drm/nouveau/nv84_fence.c
+++ b/drivers/gpu/drm/nouveau/nv84_fence.c
@@ -121,8 +121,10 @@ nv84_fence_context_del(struct nouveau_channel *chan)
}
nouveau_bo_wr32(priv->bo, chan->chid * 16 / 4, fctx->base.sequence);
+ mutex_lock(&priv->mutex);
nouveau_bo_vma_del(priv->bo, &fctx->vma_gart);
nouveau_bo_vma_del(priv->bo, &fctx->vma);
+ mutex_unlock(&priv->mutex);
nouveau_fence_context_del(&fctx->base);
chan->fence = NULL;
nouveau_fence_context_free(&fctx->base);
@@ -148,11 +150,13 @@ nv84_fence_context_new(struct nouveau_channel *chan)
fctx->base.sync32 = nv84_fence_sync32;
fctx->base.sequence = nv84_fence_read(chan);
+ mutex_lock(&priv->mutex);
ret = nouveau_bo_vma_add(priv->bo, cli->vm, &fctx->vma);
if (ret == 0) {
ret = nouveau_bo_vma_add(priv->bo_gart, cli->vm,
&fctx->vma_gart);
}
+ mutex_unlock(&priv->mutex);
/* map display semaphore buffers into channel's vm */
for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) {
@@ -232,6 +236,8 @@ nv84_fence_create(struct nouveau_drm *drm)
priv->base.context_base = fence_context_alloc(priv->base.contexts);
priv->base.uevent = true;
+ mutex_init(&priv->mutex);
+
/* Use VRAM if there is any ; otherwise fallback to system memory */
domain = drm->device.info.ram_size != 0 ? TTM_PL_FLAG_VRAM :
/*
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..46033909d950 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
@@ -36,25 +36,32 @@ 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_del_init(&alarm->head);
+ list_add(&alarm->exec, &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 */
- list_for_each_entry_safe(alarm, atemp, &exec, head) {
- list_del_init(&alarm->head);
+ /* Execute completed callbacks. */
+ list_for_each_entry_safe(alarm, atemp, &exec, exec) {
+ list_del(&alarm->exec);
alarm->func(alarm);
}
}
@@ -65,24 +72,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/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c
index 4a09947be244..3c32f095a873 100644
--- a/drivers/gpu/drm/radeon/ci_dpm.c
+++ b/drivers/gpu/drm/radeon/ci_dpm.c
@@ -776,6 +776,12 @@ bool ci_dpm_vblank_too_short(struct radeon_device *rdev)
u32 vblank_time = r600_dpm_get_vblank_time(rdev);
u32 switch_limit = pi->mem_gddr5 ? 450 : 300;
+ /* disable mclk switching if the refresh is >120Hz, even if the
+ * blanking period would allow it
+ */
+ if (r600_dpm_get_vrefresh(rdev) > 120)
+ return true;
+
if (vblank_time < switch_limit)
return true;
else
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index f81fb2641097..134874cab4c7 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -7762,7 +7762,7 @@ static inline void cik_irq_ack(struct radeon_device *rdev)
WREG32(DC_HPD5_INT_CONTROL, tmp);
}
if (rdev->irq.stat_regs.cik.disp_int_cont5 & DC_HPD6_INTERRUPT) {
- tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp = RREG32(DC_HPD6_INT_CONTROL);
tmp |= DC_HPDx_INT_ACK;
WREG32(DC_HPD6_INT_CONTROL, tmp);
}
@@ -7792,7 +7792,7 @@ static inline void cik_irq_ack(struct radeon_device *rdev)
WREG32(DC_HPD5_INT_CONTROL, tmp);
}
if (rdev->irq.stat_regs.cik.disp_int_cont5 & DC_HPD6_RX_INTERRUPT) {
- tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp = RREG32(DC_HPD6_INT_CONTROL);
tmp |= DC_HPDx_RX_INT_ACK;
WREG32(DC_HPD6_INT_CONTROL, tmp);
}
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 32491355a1d4..ba9e6ed4ae54 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -4924,7 +4924,7 @@ static void evergreen_irq_ack(struct radeon_device *rdev)
WREG32(DC_HPD5_INT_CONTROL, tmp);
}
if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) {
- tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp = RREG32(DC_HPD6_INT_CONTROL);
tmp |= DC_HPDx_INT_ACK;
WREG32(DC_HPD6_INT_CONTROL, tmp);
}
@@ -4955,7 +4955,7 @@ static void evergreen_irq_ack(struct radeon_device *rdev)
WREG32(DC_HPD5_INT_CONTROL, tmp);
}
if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_RX_INTERRUPT) {
- tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp = RREG32(DC_HPD6_INT_CONTROL);
tmp |= DC_HPDx_RX_INT_ACK;
WREG32(DC_HPD6_INT_CONTROL, tmp);
}
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index cc2fdf0be37a..0e20c08f8977 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -3945,7 +3945,7 @@ static void r600_irq_ack(struct radeon_device *rdev)
WREG32(DC_HPD5_INT_CONTROL, tmp);
}
if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD6_INTERRUPT) {
- tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp = RREG32(DC_HPD6_INT_CONTROL);
tmp |= DC_HPDx_INT_ACK;
WREG32(DC_HPD6_INT_CONTROL, tmp);
}
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index f878d6962da5..5cf3a2cbc07e 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -6335,7 +6335,7 @@ static inline void si_irq_ack(struct radeon_device *rdev)
WREG32(DC_HPD5_INT_CONTROL, tmp);
}
if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) {
- tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp = RREG32(DC_HPD6_INT_CONTROL);
tmp |= DC_HPDx_INT_ACK;
WREG32(DC_HPD6_INT_CONTROL, tmp);
}
@@ -6366,7 +6366,7 @@ static inline void si_irq_ack(struct radeon_device *rdev)
WREG32(DC_HPD5_INT_CONTROL, tmp);
}
if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_RX_INTERRUPT) {
- tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp = RREG32(DC_HPD6_INT_CONTROL);
tmp |= DC_HPDx_RX_INT_ACK;
WREG32(DC_HPD6_INT_CONTROL, tmp);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
index b6a0806b06bf..a1c68e6a689e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
@@ -368,6 +368,8 @@ static void *vmw_local_fifo_reserve(struct vmw_private *dev_priv,
return fifo_state->static_buffer;
else {
fifo_state->dynamic_buffer = vmalloc(bytes);
+ if (!fifo_state->dynamic_buffer)
+ goto out_err;
return fifo_state->dynamic_buffer;
}
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index c9c04ccccdd9..027987023400 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -1288,11 +1288,14 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret;
uint32_t size;
- uint32_t backup_handle;
+ uint32_t backup_handle = 0;
if (req->multisample_count != 0)
return -EINVAL;
+ if (req->mip_levels > DRM_VMW_MAX_MIP_LEVELS)
+ return -EINVAL;
+
if (unlikely(vmw_user_surface_size == 0))
vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
128;
@@ -1328,12 +1331,16 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
ret = vmw_user_dmabuf_lookup(tfile, req->buffer_handle,
&res->backup,
&user_srf->backup_base);
- if (ret == 0 && res->backup->base.num_pages * PAGE_SIZE <
- res->backup_size) {
- DRM_ERROR("Surface backup buffer is too small.\n");
- vmw_dmabuf_unreference(&res->backup);
- ret = -EINVAL;
- goto out_unlock;
+ if (ret == 0) {
+ if (res->backup->base.num_pages * PAGE_SIZE <
+ res->backup_size) {
+ DRM_ERROR("Surface backup buffer is too small.\n");
+ vmw_dmabuf_unreference(&res->backup);
+ ret = -EINVAL;
+ goto out_unlock;
+ } else {
+ backup_handle = req->buffer_handle;
+ }
}
} else if (req->drm_surface_flags & drm_vmw_surface_flag_create_buffer)
ret = vmw_user_dmabuf_alloc(dev_priv, tfile,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 89c7590ad121..6521ec01413e 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2139,6 +2139,11 @@ static int adreno_soft_reset(struct kgsl_device *device)
/* Reset the GPU */
_soft_reset(adreno_dev);
+ /* Clear the busy_data stats - we're starting over from scratch */
+ adreno_dev->busy_data.gpu_busy = 0;
+ adreno_dev->busy_data.vbif_ram_cycles = 0;
+ adreno_dev->busy_data.vbif_starved_ram = 0;
+
/* Set the page table back to the default page table */
adreno_ringbuffer_set_global(adreno_dev, 0);
kgsl_mmu_set_pt(&device->mmu, device->mmu.defaultpagetable);
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index dcc6651710fe..0715022be6e3 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -59,7 +59,7 @@ static const struct adreno_vbif_platform a5xx_vbif_platforms[] = {
{ adreno_is_a530, a530_vbif },
{ adreno_is_a512, a540_vbif },
{ adreno_is_a510, a530_vbif },
- { adreno_is_a508, a540_vbif },
+ { adreno_is_a508, a530_vbif },
{ adreno_is_a505, a530_vbif },
{ adreno_is_a506, a530_vbif },
};
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 55f906c9cb90..8902c3175c79 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);
}
/**
@@ -2422,7 +2443,7 @@ static void _dispatcher_power_down(struct adreno_device *adreno_dev)
mutex_unlock(&device->mutex);
}
-static void adreno_dispatcher_work(struct work_struct *work)
+static void adreno_dispatcher_work(struct kthread_work *work)
{
struct adreno_dispatcher *dispatcher =
container_of(work, struct adreno_dispatcher, work);
@@ -2482,7 +2503,7 @@ void adreno_dispatcher_schedule(struct kgsl_device *device)
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher;
- kgsl_schedule_work(&dispatcher->work);
+ queue_kthread_work(&kgsl_driver.worker, &dispatcher->work);
}
/**
@@ -2778,7 +2799,7 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev)
setup_timer(&dispatcher->fault_timer, adreno_dispatcher_fault_timer,
(unsigned long) adreno_dev);
- INIT_WORK(&dispatcher->work, adreno_dispatcher_work);
+ init_kthread_work(&dispatcher->work, adreno_dispatcher_work);
init_completion(&dispatcher->idle_gate);
complete_all(&dispatcher->idle_gate);
diff --git a/drivers/gpu/msm/adreno_dispatch.h b/drivers/gpu/msm/adreno_dispatch.h
index 72545db12f90..48f0cdc546ff 100644
--- a/drivers/gpu/msm/adreno_dispatch.h
+++ b/drivers/gpu/msm/adreno_dispatch.h
@@ -91,7 +91,7 @@ struct adreno_dispatcher {
atomic_t fault;
struct plist_head pending;
spinlock_t plist_lock;
- struct work_struct work;
+ struct kthread_work work;
struct kobject kobj;
struct completion idle_gate;
unsigned int disp_preempt_fair_sched;
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index b2def8dea954..990c9bca5127 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -255,13 +255,6 @@ static void _deferred_put(struct work_struct *work)
kgsl_mem_entry_put(entry);
}
-static inline void
-kgsl_mem_entry_put_deferred(struct kgsl_mem_entry *entry)
-{
- if (entry)
- queue_work(kgsl_driver.mem_workqueue, &entry->work);
-}
-
static inline struct kgsl_mem_entry *
kgsl_mem_entry_create(void)
{
@@ -272,7 +265,6 @@ kgsl_mem_entry_create(void)
/* put this ref in the caller functions after init */
kref_get(&entry->refcount);
- INIT_WORK(&entry->work, _deferred_put);
}
return entry;
}
@@ -1869,7 +1861,7 @@ long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv,
return -EINVAL;
ret = gpumem_free_entry(entry);
- kgsl_mem_entry_put_deferred(entry);
+ kgsl_mem_entry_put(entry);
return ret;
}
@@ -1887,7 +1879,7 @@ long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv,
return -EINVAL;
ret = gpumem_free_entry(entry);
- kgsl_mem_entry_put_deferred(entry);
+ kgsl_mem_entry_put(entry);
return ret;
}
@@ -1924,7 +1916,8 @@ static void gpuobj_free_fence_func(void *priv)
{
struct kgsl_mem_entry *entry = priv;
- kgsl_mem_entry_put_deferred(entry);
+ INIT_WORK(&entry->work, _deferred_put);
+ queue_work(kgsl_driver.mem_workqueue, &entry->work);
}
static long gpuobj_free_on_fence(struct kgsl_device_private *dev_priv,
@@ -1988,7 +1981,7 @@ long kgsl_ioctl_gpuobj_free(struct kgsl_device_private *dev_priv,
else
ret = -EINVAL;
- kgsl_mem_entry_put_deferred(entry);
+ kgsl_mem_entry_put(entry);
return ret;
}
@@ -3363,13 +3356,7 @@ long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv,
if (entry == NULL)
return -EINVAL;
- if (!kgsl_mem_entry_set_pend(entry)) {
- kgsl_mem_entry_put(entry);
- return -EBUSY;
- }
-
if (entry->memdesc.cur_bindings != 0) {
- kgsl_mem_entry_unset_pend(entry);
kgsl_mem_entry_put(entry);
return -EINVAL;
}
@@ -3378,7 +3365,7 @@ long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv,
/* One put for find_id(), one put for the kgsl_mem_entry_create() */
kgsl_mem_entry_put(entry);
- kgsl_mem_entry_put_deferred(entry);
+ kgsl_mem_entry_put(entry);
return 0;
}
@@ -3438,13 +3425,7 @@ long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv,
if (entry == NULL)
return -EINVAL;
- if (!kgsl_mem_entry_set_pend(entry)) {
- kgsl_mem_entry_put(entry);
- return -EBUSY;
- }
-
if (entry->bind_tree.rb_node != NULL) {
- kgsl_mem_entry_unset_pend(entry);
kgsl_mem_entry_put(entry);
return -EINVAL;
}
@@ -3453,7 +3434,7 @@ long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv,
/* One put for find_id(), one put for the kgsl_mem_entry_create() */
kgsl_mem_entry_put(entry);
- kgsl_mem_entry_put_deferred(entry);
+ kgsl_mem_entry_put(entry);
return 0;
}
@@ -4719,6 +4700,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);
@@ -4863,6 +4845,8 @@ static void kgsl_core_exit(void)
static int __init kgsl_core_init(void)
{
int result = 0;
+ struct sched_param param = { .sched_priority = 2 };
+
/* alloc major and minor device numbers */
result = alloc_chrdev_region(&kgsl_driver.major, 0, KGSL_DEVICE_MAX,
"kgsl");
@@ -4926,7 +4910,19 @@ static int __init kgsl_core_init(void)
WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry",
- WQ_MEM_RECLAIM, 0);
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
+
+ init_kthread_worker(&kgsl_driver.worker);
+
+ kgsl_driver.worker_thread = kthread_run(kthread_worker_fn,
+ &kgsl_driver.worker, "kgsl_worker_thread");
+
+ if (IS_ERR(kgsl_driver.worker_thread)) {
+ pr_err("unable to start kgsl thread\n");
+ goto err;
+ }
+
+ sched_setscheduler(kgsl_driver.worker_thread, SCHED_FIFO, &param);
kgsl_events_init();
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 2a9ac899725c..faf38d1d2293 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -26,6 +26,7 @@
#include <linux/mm.h>
#include <linux/dma-attrs.h>
#include <linux/uaccess.h>
+#include <linux/kthread.h>
#include <asm/cacheflush.h>
/*
@@ -152,6 +153,8 @@ struct kgsl_driver {
unsigned int full_cache_threshold;
struct workqueue_struct *workqueue;
struct workqueue_struct *mem_workqueue;
+ struct kthread_worker worker;
+ struct task_struct *worker_thread;
};
extern struct kgsl_driver kgsl_driver;
@@ -301,7 +304,7 @@ struct kgsl_event {
void *priv;
struct list_head node;
unsigned int created;
- struct work_struct work;
+ struct kthread_work work;
int result;
struct kgsl_event_group *group;
};
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_events.c b/drivers/gpu/msm/kgsl_events.c
index 6e8abf36c50f..859511baba12 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -32,7 +32,7 @@ static inline void signal_event(struct kgsl_device *device,
{
list_del(&event->node);
event->result = result;
- queue_work(device->events_wq, &event->work);
+ queue_kthread_work(&kgsl_driver.worker, &event->work);
}
/**
@@ -42,7 +42,7 @@ static inline void signal_event(struct kgsl_device *device,
* Each event callback has its own work struct and is run on a event specific
* workqeuue. This is the worker that queues up the event callback function.
*/
-static void _kgsl_event_worker(struct work_struct *work)
+static void _kgsl_event_worker(struct kthread_work *work)
{
struct kgsl_event *event = container_of(work, struct kgsl_event, work);
int id = KGSL_CONTEXT_ID(event->context);
@@ -282,7 +282,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group,
event->created = jiffies;
event->group = group;
- INIT_WORK(&event->work, _kgsl_event_worker);
+ init_kthread_work(&event->work, _kgsl_event_worker);
trace_kgsl_register_event(KGSL_CONTEXT_ID(context), timestamp, func);
@@ -297,7 +297,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group,
if (timestamp_cmp(retired, timestamp) >= 0) {
event->result = KGSL_EVENT_RETIRED;
- queue_work(device->events_wq, &event->work);
+ queue_kthread_work(&kgsl_driver.worker, &event->work);
spin_unlock(&group->lock);
return 0;
}
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index bb92b8b79d93..c31a85b07447 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -280,6 +280,17 @@ static int kgsl_pool_idx_lookup(unsigned int order)
return -ENOMEM;
}
+static int kgsl_pool_get_retry_order(unsigned int order)
+{
+ int i;
+
+ for (i = kgsl_num_pools-1; i > 0; i--)
+ if (order >= kgsl_pools[i].pool_order)
+ return kgsl_pools[i].pool_order;
+
+ return 0;
+}
+
/**
* kgsl_pool_alloc_page() - Allocate a page of requested size
* @page_size: Size of the page to be allocated
@@ -326,7 +337,7 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
if (pool == NULL) {
/* Retry with lower order pages */
if (order > 0) {
- size = PAGE_SIZE << --order;
+ size = PAGE_SIZE << kgsl_pool_get_retry_order(order);
goto eagain;
} else {
/*
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/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index fa95b4dfe718..27733b068434 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -20,6 +20,7 @@
#include <linux/scatterlist.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/secure_buffer.h>
+#include <linux/ratelimit.h>
#include "kgsl.h"
#include "kgsl_sharedmem.h"
@@ -623,6 +624,9 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset,
uint64_t size, unsigned int op)
{
void *addr = NULL;
+ struct sg_table *sgt = NULL;
+ struct scatterlist *sg;
+ unsigned int i, pos = 0;
int ret = 0;
if (size == 0 || size > UINT_MAX)
@@ -650,40 +654,38 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset,
* If the buffer is not to mapped to kernel, perform cache
* operations after mapping to kernel.
*/
- if (memdesc->sgt != NULL) {
- struct scatterlist *sg;
- unsigned int i, pos = 0;
+ if (memdesc->sgt != NULL)
+ sgt = memdesc->sgt;
+ else {
+ if (memdesc->pages == NULL)
+ return ret;
+
+ sgt = kgsl_alloc_sgt_from_pages(memdesc);
+ if (IS_ERR(sgt))
+ return PTR_ERR(sgt);
+ }
- for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) {
- uint64_t sg_offset, sg_left;
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ uint64_t sg_offset, sg_left;
- if (offset >= (pos + sg->length)) {
- pos += sg->length;
- continue;
- }
- sg_offset = offset > pos ? offset - pos : 0;
- sg_left = (sg->length - sg_offset > size) ? size :
- sg->length - sg_offset;
- ret = kgsl_do_cache_op(sg_page(sg), NULL, sg_offset,
- sg_left, op);
- size -= sg_left;
- if (size == 0)
- break;
+ if (offset >= (pos + sg->length)) {
pos += sg->length;
+ continue;
}
- } else if (memdesc->pages != NULL) {
- addr = vmap(memdesc->pages, memdesc->page_count,
- VM_IOREMAP, pgprot_writecombine(PAGE_KERNEL));
- if (addr == NULL)
- return -ENOMEM;
+ sg_offset = offset > pos ? offset - pos : 0;
+ sg_left = (sg->length - sg_offset > size) ? size :
+ sg->length - sg_offset;
+ ret = kgsl_do_cache_op(sg_page(sg), NULL, sg_offset,
+ sg_left, op);
+ size -= sg_left;
+ if (size == 0)
+ break;
+ pos += sg->length;
+ }
- /* Make sure the offset + size do not overflow the address */
- if (addr + ((size_t) offset + (size_t) size) < addr)
- return -ERANGE;
+ if (memdesc->sgt == NULL)
+ kgsl_free_sgt(sgt);
- ret = kgsl_do_cache_op(NULL, addr, offset, size, op);
- vunmap(addr);
- }
return ret;
}
EXPORT_SYMBOL(kgsl_cache_range_op);
@@ -698,6 +700,10 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
size_t len;
unsigned int align;
+ static DEFINE_RATELIMIT_STATE(_rs,
+ DEFAULT_RATELIMIT_INTERVAL,
+ DEFAULT_RATELIMIT_BURST);
+
size = PAGE_ALIGN(size);
if (size == 0 || size > UINT_MAX)
return -EINVAL;
@@ -760,7 +766,8 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
*/
memdesc->size = (size - len);
- if (sharedmem_noretry_flag != true)
+ if (sharedmem_noretry_flag != true &&
+ __ratelimit(&_rs))
KGSL_CORE_ERR(
"Out of memory: only allocated %lldKB of %lldKB requested\n",
(size - len) >> 10, size >> 10);
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 1a2032c2c1fb..690a9f0fa042 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -28,6 +28,8 @@
#define UHID_NAME "uhid"
#define UHID_BUFSIZE 32
+static DEFINE_MUTEX(uhid_open_mutex);
+
struct uhid_device {
struct mutex devlock;
bool running;
@@ -142,15 +144,26 @@ static void uhid_hid_stop(struct hid_device *hid)
static int uhid_hid_open(struct hid_device *hid)
{
struct uhid_device *uhid = hid->driver_data;
+ int retval = 0;
- return uhid_queue_event(uhid, UHID_OPEN);
+ mutex_lock(&uhid_open_mutex);
+ if (!hid->open++) {
+ retval = uhid_queue_event(uhid, UHID_OPEN);
+ if (retval)
+ hid->open--;
+ }
+ mutex_unlock(&uhid_open_mutex);
+ return retval;
}
static void uhid_hid_close(struct hid_device *hid)
{
struct uhid_device *uhid = hid->driver_data;
- uhid_queue_event(uhid, UHID_CLOSE);
+ mutex_lock(&uhid_open_mutex);
+ if (!--hid->open)
+ uhid_queue_event(uhid, UHID_CLOSE);
+ mutex_unlock(&uhid_open_mutex);
}
static int uhid_hid_parse(struct hid_device *hid)
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 35e3fd9fadf6..b62c50d1b1e4 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -1440,37 +1440,38 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
{
unsigned char *data = wacom->data;
- if (wacom->pen_input)
+ if (wacom->pen_input) {
dev_dbg(wacom->pen_input->dev.parent,
"%s: received report #%d\n", __func__, data[0]);
- else if (wacom->touch_input)
+
+ if (len == WACOM_PKGLEN_PENABLED ||
+ data[0] == WACOM_REPORT_PENABLED)
+ return wacom_tpc_pen(wacom);
+ }
+ else if (wacom->touch_input) {
dev_dbg(wacom->touch_input->dev.parent,
"%s: received report #%d\n", __func__, data[0]);
- switch (len) {
- case WACOM_PKGLEN_TPC1FG:
- return wacom_tpc_single_touch(wacom, len);
+ switch (len) {
+ case WACOM_PKGLEN_TPC1FG:
+ return wacom_tpc_single_touch(wacom, len);
- case WACOM_PKGLEN_TPC2FG:
- return wacom_tpc_mt_touch(wacom);
+ case WACOM_PKGLEN_TPC2FG:
+ return wacom_tpc_mt_touch(wacom);
- case WACOM_PKGLEN_PENABLED:
- return wacom_tpc_pen(wacom);
+ default:
+ switch (data[0]) {
+ case WACOM_REPORT_TPC1FG:
+ case WACOM_REPORT_TPCHID:
+ case WACOM_REPORT_TPCST:
+ case WACOM_REPORT_TPC1FGE:
+ return wacom_tpc_single_touch(wacom, len);
- default:
- switch (data[0]) {
- case WACOM_REPORT_TPC1FG:
- case WACOM_REPORT_TPCHID:
- case WACOM_REPORT_TPCST:
- case WACOM_REPORT_TPC1FGE:
- return wacom_tpc_single_touch(wacom, len);
-
- case WACOM_REPORT_TPCMT:
- case WACOM_REPORT_TPCMT2:
- return wacom_mt_touch(wacom);
+ case WACOM_REPORT_TPCMT:
+ case WACOM_REPORT_TPCMT2:
+ return wacom_mt_touch(wacom);
- case WACOM_REPORT_PENABLED:
- return wacom_tpc_pen(wacom);
+ }
}
}
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index 7f98d9f527b9..1f042fa56eea 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.c
@@ -1310,7 +1310,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete);
if (!ret && ctrl->xfer.rx_cnt)
- i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.rx_complete);
+ ret = i2c_msm_xfer_wait_for_completion(ctrl,
+ &ctrl->xfer.rx_complete);
dma_xfer_end:
/* free scatter-gather lists */
@@ -1716,9 +1717,7 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
void __iomem *base = ctrl->rsrcs.base;
struct i2c_msm_xfer *xfer = &ctrl->xfer;
struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk;
- u32 i2c_status = 0;
u32 err_flags = 0;
- u32 qup_op = 0;
u32 clr_flds = 0;
bool log_event = false;
bool signal_complete = false;
@@ -1731,24 +1730,24 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
return IRQ_HANDLED;
}
- i2c_status = readl_relaxed(base + QUP_I2C_STATUS);
- err_flags = readl_relaxed(base + QUP_ERROR_FLAGS);
- qup_op = readl_relaxed(base + QUP_OPERATIONAL);
+ ctrl->i2c_sts_reg = readl_relaxed(base + QUP_I2C_STATUS);
+ err_flags = readl_relaxed(base + QUP_ERROR_FLAGS);
+ ctrl->qup_op_reg = readl_relaxed(base + QUP_OPERATIONAL);
- if (i2c_status & QUP_MSTR_STTS_ERR_MASK) {
+ if (ctrl->i2c_sts_reg & QUP_MSTR_STTS_ERR_MASK) {
signal_complete = true;
log_event = true;
/*
* If there is more than 1 error here, last one sticks.
* The order of the error set here matters.
*/
- if (i2c_status & QUP_ARB_LOST)
+ if (ctrl->i2c_sts_reg & QUP_ARB_LOST)
ctrl->xfer.err = I2C_MSM_ERR_ARB_LOST;
- if (i2c_status & QUP_BUS_ERROR)
+ if (ctrl->i2c_sts_reg & QUP_BUS_ERROR)
ctrl->xfer.err = I2C_MSM_ERR_BUS_ERR;
- if (i2c_status & QUP_PACKET_NACKED)
+ if (ctrl->i2c_sts_reg & QUP_PACKET_NACKED)
ctrl->xfer.err = I2C_MSM_ERR_NACK;
}
@@ -1761,7 +1760,7 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
i2c_msm_dbg_qup_reg_dump(ctrl);
/* clear interrupts fields */
- clr_flds = i2c_status & QUP_MSTR_STTS_ERR_MASK;
+ clr_flds = ctrl->i2c_sts_reg & QUP_MSTR_STTS_ERR_MASK;
if (clr_flds) {
writel_relaxed(clr_flds, base + QUP_I2C_STATUS);
need_wmb = true;
@@ -1773,7 +1772,9 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
need_wmb = true;
}
- clr_flds = qup_op & (QUP_OUTPUT_SERVICE_FLAG | QUP_INPUT_SERVICE_FLAG);
+ clr_flds = ctrl->qup_op_reg &
+ (QUP_OUTPUT_SERVICE_FLAG |
+ QUP_INPUT_SERVICE_FLAG);
if (clr_flds) {
writel_relaxed(clr_flds, base + QUP_OPERATIONAL);
need_wmb = true;
@@ -1814,25 +1815,25 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
/* handle data completion */
if (xfer->mode_id == I2C_MSM_XFER_MODE_BLOCK) {
/* block ready for writing */
- if (qup_op & QUP_OUTPUT_SERVICE_FLAG) {
+ if (ctrl->qup_op_reg & QUP_OUTPUT_SERVICE_FLAG) {
log_event = true;
- if (qup_op & QUP_OUT_BLOCK_WRITE_REQ)
+ if (ctrl->qup_op_reg & QUP_OUT_BLOCK_WRITE_REQ)
complete(&blk->wait_tx_blk);
- if ((qup_op & blk->complete_mask)
+ if ((ctrl->qup_op_reg & blk->complete_mask)
== blk->complete_mask) {
log_event = true;
signal_complete = true;
}
}
/* block ready for reading */
- if (qup_op & QUP_INPUT_SERVICE_FLAG) {
+ if (ctrl->qup_op_reg & QUP_INPUT_SERVICE_FLAG) {
log_event = true;
complete(&blk->wait_rx_blk);
}
} else {
/* for FIFO/DMA Mode*/
- if (qup_op & QUP_MAX_INPUT_DONE_FLAG) {
+ if (ctrl->qup_op_reg & QUP_MAX_INPUT_DONE_FLAG) {
log_event = true;
/*
* If last transaction is an input then the entire
@@ -1850,7 +1851,7 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
* here QUP_OUTPUT_SERVICE_FLAG and assumes that
* QUP_MAX_OUTPUT_DONE_FLAG.
*/
- if (qup_op & (QUP_OUTPUT_SERVICE_FLAG |
+ if (ctrl->qup_op_reg & (QUP_OUTPUT_SERVICE_FLAG |
QUP_MAX_OUTPUT_DONE_FLAG)) {
log_event = true;
/*
@@ -1863,13 +1864,11 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
}
isr_end:
- if (ctrl->xfer.err || (ctrl->dbgfs.dbg_lvl >= MSM_DBG))
- i2c_msm_dbg_dump_diag(ctrl, true, i2c_status, qup_op);
-
if (log_event || (ctrl->dbgfs.dbg_lvl >= MSM_DBG))
i2c_msm_prof_evnt_add(ctrl, MSM_PROF,
I2C_MSM_IRQ_END,
- i2c_status, qup_op, err_flags);
+ ctrl->i2c_sts_reg, ctrl->qup_op_reg,
+ err_flags);
if (signal_complete)
complete(&ctrl->xfer.complete);
@@ -2078,8 +2077,12 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl,
xfer->timeout, time_left, 0);
} else {
/* return an error if one detected by ISR */
- if (xfer->err)
+ if (ctrl->xfer.err ||
+ (ctrl->dbgfs.dbg_lvl >= MSM_DBG)) {
+ i2c_msm_dbg_dump_diag(ctrl, true,
+ ctrl->i2c_sts_reg, ctrl->qup_op_reg);
ret = -(xfer->err);
+ }
i2c_msm_prof_evnt_add(ctrl, MSM_DBG, I2C_MSM_COMPLT_OK,
xfer->timeout, time_left, 0);
}
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 630bce68bf38..b61db9db3ca5 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -54,7 +54,7 @@
#define SMBSLVDAT (0xC + piix4_smba)
/* count for request_region */
-#define SMBIOSIZE 8
+#define SMBIOSIZE 9
/* PCI Address Constants */
#define SMBBA 0x090
diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c
index 0ed77eeff31e..a2e3dd715380 100644
--- a/drivers/i2c/busses/i2c-tiny-usb.c
+++ b/drivers/i2c/busses/i2c-tiny-usb.c
@@ -178,22 +178,39 @@ static int usb_read(struct i2c_adapter *adapter, int cmd,
int value, int index, void *data, int len)
{
struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data;
+ void *dmadata = kmalloc(len, GFP_KERNEL);
+ int ret;
+
+ if (!dmadata)
+ return -ENOMEM;
/* do control transfer */
- return usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0),
+ ret = usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0),
cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE |
- USB_DIR_IN, value, index, data, len, 2000);
+ USB_DIR_IN, value, index, dmadata, len, 2000);
+
+ memcpy(data, dmadata, len);
+ kfree(dmadata);
+ return ret;
}
static int usb_write(struct i2c_adapter *adapter, int cmd,
int value, int index, void *data, int len)
{
struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data;
+ void *dmadata = kmemdup(data, len, GFP_KERNEL);
+ int ret;
+
+ if (!dmadata)
+ return -ENOMEM;
/* do control transfer */
- return usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0),
+ ret = usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0),
cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- value, index, data, len, 2000);
+ value, index, dmadata, len, 2000);
+
+ kfree(dmadata);
+ return ret;
}
static void i2c_tiny_usb_free(struct i2c_tiny_usb *dev)
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/light/ltr501.c b/drivers/iio/light/ltr501.c
index 6bf89d8f3741..b9d1e5c58ec5 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -74,9 +74,9 @@ static const int int_time_mapping[] = {100000, 50000, 200000, 400000};
static const struct reg_field reg_field_it =
REG_FIELD(LTR501_ALS_MEAS_RATE, 3, 4);
static const struct reg_field reg_field_als_intr =
- REG_FIELD(LTR501_INTR, 0, 0);
-static const struct reg_field reg_field_ps_intr =
REG_FIELD(LTR501_INTR, 1, 1);
+static const struct reg_field reg_field_ps_intr =
+ REG_FIELD(LTR501_INTR, 0, 0);
static const struct reg_field reg_field_als_rate =
REG_FIELD(LTR501_ALS_MEAS_RATE, 0, 2);
static const struct reg_field reg_field_ps_rate =
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index a0aedda7dfd7..420478924a0c 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -40,9 +40,9 @@
#define AS3935_AFE_PWR_BIT BIT(0)
#define AS3935_INT 0x03
-#define AS3935_INT_MASK 0x07
+#define AS3935_INT_MASK 0x0f
#define AS3935_EVENT_INT BIT(3)
-#define AS3935_NOISE_INT BIT(1)
+#define AS3935_NOISE_INT BIT(0)
#define AS3935_DATA 0x07
#define AS3935_DATA_MASK 0x3F
@@ -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);
@@ -264,8 +263,6 @@ static irqreturn_t as3935_interrupt_handler(int irq, void *private)
static void calibrate_as3935(struct as3935_state *st)
{
- mutex_lock(&st->lock);
-
/* mask disturber interrupt bit */
as3935_write(st, AS3935_INT, BIT(5));
@@ -275,8 +272,6 @@ static void calibrate_as3935(struct as3935_state *st)
mdelay(2);
as3935_write(st, AS3935_TUNE_CAP, (st->tune_cap / TUNE_CAP_DIV));
-
- mutex_unlock(&st->lock);
}
#ifdef CONFIG_PM_SLEEP
@@ -313,6 +308,8 @@ static int as3935_resume(struct device *dev)
val &= ~AS3935_AFE_PWR_BIT;
ret = as3935_write(st, AS3935_AFE_GAIN, val);
+ calibrate_as3935(st);
+
err_resume:
mutex_unlock(&st->lock);
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/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c
index e6b7556d5221..cbc4216091c9 100644
--- a/drivers/infiniband/hw/qib/qib_rc.c
+++ b/drivers/infiniband/hw/qib/qib_rc.c
@@ -2088,8 +2088,10 @@ send_last:
ret = qib_get_rwqe(qp, 1);
if (ret < 0)
goto nack_op_err;
- if (!ret)
+ if (!ret) {
+ qib_put_ss(&qp->r_sge);
goto rnr_nak;
+ }
wc.ex.imm_data = ohdr->u.rc.imm_data;
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
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/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 1a2b2620421e..6f4dc0fd2ca3 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1122,8 +1122,10 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
* Avatar AVIU-145A2 0x361f00 ? clickpad
* Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons
+ * Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons
* Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons
* Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons
+ * Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons
* Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons
* Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**)
* Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons
@@ -1529,6 +1531,13 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
},
},
{
+ /* Fujitsu LIFEBOOK E546 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E546"),
+ },
+ },
+ {
/* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
@@ -1550,6 +1559,13 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
},
},
{
+ /* Fujitsu LIFEBOOK E557 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E557"),
+ },
+ },
+ {
/* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
diff --git a/drivers/input/touchscreen/synaptics_dsx/Kconfig b/drivers/input/touchscreen/synaptics_dsx/Kconfig
index 86263fddacea..18d473969261 100644
--- a/drivers/input/touchscreen/synaptics_dsx/Kconfig
+++ b/drivers/input/touchscreen/synaptics_dsx/Kconfig
@@ -50,17 +50,6 @@ config TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_core.
-config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21
- tristate "Synaptics DSX touchscreen RMI device module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21
- help
- Say Y here to enable support for direct RMI register access.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_rmi_dev.
-
config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21
tristate "Synaptics DSX touchscreen firmware update module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21
@@ -72,15 +61,4 @@ config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_fw_update.
-config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v21
- tristate "Synaptics DSX touchscreen proximity module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21
- help
- Say Y here to enable support for proximity functionalities.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_proximity.
-
endif
diff --git a/drivers/input/touchscreen/synaptics_dsx/Makefile b/drivers/input/touchscreen/synaptics_dsx/Makefile
index b35b222d5ae2..0bffb8da94ea 100644
--- a/drivers/input/touchscreen/synaptics_dsx/Makefile
+++ b/drivers/input/touchscreen/synaptics_dsx/Makefile
@@ -7,6 +7,4 @@
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C_v21) += synaptics_dsx_i2c.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI_v21) += synaptics_dsx_spi.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21) += synaptics_dsx_core.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21) += synaptics_dsx_rmi_dev.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21) += synaptics_dsx_fw_update.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v21) += synaptics_dsx_proximity.o
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
index daa35845fc0a..93c9c3c373b8 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
@@ -1495,7 +1495,7 @@ static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data,
return retval;
}
-static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler,
+static int synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler,
struct synaptics_rmi4_fn_desc *fd,
unsigned int intr_count)
{
@@ -1503,6 +1503,12 @@ static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler,
unsigned char intr_offset;
fhandler->intr_reg_num = (intr_count + 7) / 8;
+ if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) {
+ fhandler->intr_reg_num = 0;
+ fhandler->num_of_data_sources = 0;
+ fhandler->intr_mask = 0;
+ return -EINVAL;
+ }
if (fhandler->intr_reg_num != 0)
fhandler->intr_reg_num -= 1;
@@ -1515,7 +1521,7 @@ static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler,
ii++)
fhandler->intr_mask |= 1 << ii;
- return;
+ return 0;
}
static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data,
@@ -1523,12 +1529,17 @@ static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data,
struct synaptics_rmi4_fn_desc *fd,
unsigned int intr_count)
{
+ int retval;
+
fhandler->fn_number = fd->fn_number;
fhandler->num_of_data_sources = fd->intr_src_count;
fhandler->data = NULL;
fhandler->extra = NULL;
- synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+ retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+ if (retval < 0)
+ return retval;
+
rmi4_data->f01_query_base_addr = fd->query_base_addr;
rmi4_data->f01_ctrl_base_addr = fd->ctrl_base_addr;
@@ -1653,7 +1664,9 @@ static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data,
if (retval < 0)
return retval;
- synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+ retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+ if (retval < 0)
+ return retval;
abs_data_size = query[5] & MASK_2BIT;
abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0));
@@ -1934,7 +1947,9 @@ static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
if (retval < 0)
goto free_function_handler_mem;
- synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+ retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+ if (retval < 0)
+ return retval;
/* Allocate memory for finger data storage space */
fhandler->data_size = num_of_fingers * size_of_2d_data;
@@ -2092,7 +2107,9 @@ static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data,
fhandler->fn_number = fd->fn_number;
fhandler->num_of_data_sources = fd->intr_src_count;
- synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+ retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+ if (retval < 0)
+ return retval;
retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler);
if (retval < 0)
@@ -2491,6 +2508,8 @@ flash_prog_mode:
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Number of interrupt registers = %d\n",
__func__, rmi4_data->num_of_intr_regs);
+ if (rmi4_data->num_of_intr_regs >= MAX_INTR_REGISTERS)
+ return -EINVAL;
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f01_query_base_addr,
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c
deleted file mode 100755
index 99c05e6845c0..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c
+++ /dev/null
@@ -1,671 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012 Synaptics Incorporated
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2.h>
-#include "synaptics_dsx_core.h"
-
-#define PROX_PHYS_NAME "synaptics_dsx/input1"
-
-#define HOVER_Z_MAX (255)
-
-#define HOVERING_FINGER_EN (1 << 4)
-
-static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static struct device_attribute attrs[] = {
- __ATTR(hover_finger_en, (S_IRUGO | S_IWUGO),
- synaptics_rmi4_hover_finger_en_show,
- synaptics_rmi4_hover_finger_en_store),
-};
-
-struct synaptics_rmi4_f12_query_5 {
- union {
- struct {
- unsigned char size_of_query6;
- struct {
- unsigned char ctrl0_is_present:1;
- unsigned char ctrl1_is_present:1;
- unsigned char ctrl2_is_present:1;
- unsigned char ctrl3_is_present:1;
- unsigned char ctrl4_is_present:1;
- unsigned char ctrl5_is_present:1;
- unsigned char ctrl6_is_present:1;
- unsigned char ctrl7_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl8_is_present:1;
- unsigned char ctrl9_is_present:1;
- unsigned char ctrl10_is_present:1;
- unsigned char ctrl11_is_present:1;
- unsigned char ctrl12_is_present:1;
- unsigned char ctrl13_is_present:1;
- unsigned char ctrl14_is_present:1;
- unsigned char ctrl15_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl16_is_present:1;
- unsigned char ctrl17_is_present:1;
- unsigned char ctrl18_is_present:1;
- unsigned char ctrl19_is_present:1;
- unsigned char ctrl20_is_present:1;
- unsigned char ctrl21_is_present:1;
- unsigned char ctrl22_is_present:1;
- unsigned char ctrl23_is_present:1;
- } __packed;
- };
- unsigned char data[4];
- };
-};
-
-struct synaptics_rmi4_f12_query_8 {
- union {
- struct {
- unsigned char size_of_query9;
- struct {
- unsigned char data0_is_present:1;
- unsigned char data1_is_present:1;
- unsigned char data2_is_present:1;
- unsigned char data3_is_present:1;
- unsigned char data4_is_present:1;
- unsigned char data5_is_present:1;
- unsigned char data6_is_present:1;
- unsigned char data7_is_present:1;
- } __packed;
- };
- unsigned char data[2];
- };
-};
-
-struct prox_finger_data {
- union {
- struct {
- unsigned char object_type_and_status;
- unsigned char x_lsb;
- unsigned char x_msb;
- unsigned char y_lsb;
- unsigned char y_msb;
- unsigned char z;
- } __packed;
- unsigned char proximity_data[6];
- };
-};
-
-struct synaptics_rmi4_prox_handle {
- bool hover_finger_present;
- bool hover_finger_en;
- unsigned char intr_mask;
- unsigned short query_base_addr;
- unsigned short control_base_addr;
- unsigned short data_base_addr;
- unsigned short command_base_addr;
- unsigned short hover_finger_en_addr;
- unsigned short hover_finger_data_addr;
- struct input_dev *prox_dev;
- struct prox_finger_data *finger_data;
- struct synaptics_rmi4_data *rmi4_data;
-};
-
-static struct synaptics_rmi4_prox_handle *prox;
-
-DECLARE_COMPLETION(prox_remove_complete);
-
-static void prox_hover_finger_lift(void)
-{
- input_report_key(prox->prox_dev, BTN_TOUCH, 0);
- input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 0);
- input_sync(prox->prox_dev);
- prox->hover_finger_present = false;
-
- return;
-}
-
-static void prox_hover_finger_report(void)
-{
- int retval;
- int x;
- int y;
- int z;
- struct prox_finger_data *data;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- data = prox->finger_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- prox->hover_finger_data_addr,
- data->proximity_data,
- sizeof(data->proximity_data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read hovering finger data\n",
- __func__);
- return;
- }
-
- if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) {
- if (prox->hover_finger_present)
- prox_hover_finger_lift();
-
- return;
- }
-
- x = (data->x_msb << 8) | (data->x_lsb);
- y = (data->y_msb << 8) | (data->y_lsb);
- z = HOVER_Z_MAX - data->z;
-
- input_report_key(prox->prox_dev, BTN_TOUCH, 0);
- input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1);
- input_report_abs(prox->prox_dev, ABS_X, x);
- input_report_abs(prox->prox_dev, ABS_Y, y);
- input_report_abs(prox->prox_dev, ABS_DISTANCE, z);
-
- input_sync(prox->prox_dev);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: x = %d y = %d z = %d\n",
- __func__, x, y, z);
-
- prox->hover_finger_present = true;
-
- return;
-}
-
-static int prox_set_hover_finger_en(void)
-{
- int retval;
- unsigned char object_report_enable;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- prox->hover_finger_en_addr,
- &object_report_enable,
- sizeof(object_report_enable));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read from object report enable register\n",
- __func__);
- return retval;
- }
-
- if (prox->hover_finger_en)
- object_report_enable |= HOVERING_FINGER_EN;
- else
- object_report_enable &= ~HOVERING_FINGER_EN;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- prox->hover_finger_en_addr,
- &object_report_enable,
- sizeof(object_report_enable));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write to object report enable register\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static void prox_set_params(void)
-{
- input_set_abs_params(prox->prox_dev, ABS_X, 0,
- prox->rmi4_data->sensor_max_x, 0, 0);
- input_set_abs_params(prox->prox_dev, ABS_Y, 0,
- prox->rmi4_data->sensor_max_y, 0, 0);
- input_set_abs_params(prox->prox_dev, ABS_DISTANCE, 0,
- HOVER_Z_MAX, 0, 0);
-
- return;
-}
-
-static int prox_reg_init(void)
-{
- int retval;
- unsigned char ctrl_23_offset;
- unsigned char data_1_offset;
- struct synaptics_rmi4_f12_query_5 query_5;
- struct synaptics_rmi4_f12_query_8 query_8;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- prox->query_base_addr + 5,
- query_5.data,
- sizeof(query_5.data));
- if (retval < 0)
- return retval;
-
- ctrl_23_offset = query_5.ctrl0_is_present +
- query_5.ctrl1_is_present +
- query_5.ctrl2_is_present +
- query_5.ctrl3_is_present +
- query_5.ctrl4_is_present +
- query_5.ctrl5_is_present +
- query_5.ctrl6_is_present +
- query_5.ctrl7_is_present +
- query_5.ctrl8_is_present +
- query_5.ctrl9_is_present +
- query_5.ctrl10_is_present +
- query_5.ctrl11_is_present +
- query_5.ctrl12_is_present +
- query_5.ctrl13_is_present +
- query_5.ctrl14_is_present +
- query_5.ctrl15_is_present +
- query_5.ctrl16_is_present +
- query_5.ctrl17_is_present +
- query_5.ctrl18_is_present +
- query_5.ctrl19_is_present +
- query_5.ctrl20_is_present +
- query_5.ctrl21_is_present +
- query_5.ctrl22_is_present;
-
- prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- prox->query_base_addr + 8,
- query_8.data,
- sizeof(query_8.data));
- if (retval < 0)
- return retval;
-
- data_1_offset = query_8.data0_is_present;
- prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset;
-
- return retval;
-}
-
-static int prox_scan_pdt(void)
-{
- int retval;
- unsigned char ii;
- unsigned char page;
- unsigned char intr_count = 0;
- unsigned char intr_off;
- unsigned char intr_src;
- unsigned short addr;
- struct synaptics_rmi4_fn_desc fd;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- for (page = 0; page < PAGES_TO_SERVICE; page++) {
- for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
- addr |= (page << 8);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- addr,
- (unsigned char *)&fd,
- sizeof(fd));
- if (retval < 0)
- return retval;
-
- addr &= ~(MASK_8BIT << 8);
-
- if (fd.fn_number) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Found F%02x\n",
- __func__, fd.fn_number);
- switch (fd.fn_number) {
- case SYNAPTICS_RMI4_F12:
- goto f12_found;
- break;
- }
- } else {
- break;
- }
-
- intr_count += (fd.intr_src_count & MASK_3BIT);
- }
- }
-
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F12\n",
- __func__);
- return -EINVAL;
-
-f12_found:
- prox->query_base_addr = fd.query_base_addr | (page << 8);
- prox->control_base_addr = fd.ctrl_base_addr | (page << 8);
- prox->data_base_addr = fd.data_base_addr | (page << 8);
- prox->command_base_addr = fd.cmd_base_addr | (page << 8);
-
- retval = prox_reg_init();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to initialize proximity registers\n",
- __func__);
- return retval;
- }
-
- prox->intr_mask = 0;
- intr_src = fd.intr_src_count;
- intr_off = intr_count % 8;
- for (ii = intr_off;
- ii < ((intr_src & MASK_3BIT) +
- intr_off);
- ii++) {
- prox->intr_mask |= 1 << ii;
- }
-
- rmi4_data->intr_mask[0] |= prox->intr_mask;
-
- addr = rmi4_data->f01_ctrl_base_addr + 1;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- addr,
- &(rmi4_data->intr_mask[0]),
- sizeof(rmi4_data->intr_mask[0]));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set interrupt enable bit\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (!prox)
- return -ENODEV;
-
- return snprintf(buf, PAGE_SIZE, "%u\n",
- prox->hover_finger_en);
-}
-
-static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- if (!prox)
- return -ENODEV;
-
- if (sscanf(buf, "%x", &input) != 1)
- return -EINVAL;
-
- if (input == 1)
- prox->hover_finger_en = true;
- else if (input == 0)
- prox->hover_finger_en = false;
- else
- return -EINVAL;
-
- retval = prox_set_hover_finger_en();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to change hovering finger enable setting\n",
- __func__);
- return retval;
- }
-
- return count;
-}
-
-int synaptics_rmi4_prox_hover_finger_en(bool enable)
-{
- int retval;
-
- if (!prox)
- return -ENODEV;
-
- prox->hover_finger_en = enable;
-
- retval = prox_set_hover_finger_en();
- if (retval < 0)
- return retval;
-
- return 0;
-}
-EXPORT_SYMBOL(synaptics_rmi4_prox_hover_finger_en);
-
-static void synaptics_rmi4_prox_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
-{
- if (!prox)
- return;
-
- if (prox->intr_mask & intr_mask)
- prox_hover_finger_report();
-
- return;
-}
-
-static int synaptics_rmi4_prox_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char attr_count;
-
- prox = kzalloc(sizeof(*prox), GFP_KERNEL);
- if (!prox) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for prox\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- prox->finger_data = kzalloc(sizeof(*(prox->finger_data)), GFP_KERNEL);
- if (!prox->finger_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for finger_data\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_prox;
- }
-
- prox->rmi4_data = rmi4_data;
-
- retval = prox_scan_pdt();
- if (retval < 0)
- goto exit_free_finger_data;
-
- prox->hover_finger_en = true;
-
- retval = prox_set_hover_finger_en();
- if (retval < 0)
- return retval;
-
- prox->prox_dev = input_allocate_device();
- if (prox->prox_dev == NULL) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate proximity device\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_finger_data;
- }
-
- prox->prox_dev->name = PLATFORM_DRIVER_NAME;
- prox->prox_dev->phys = PROX_PHYS_NAME;
- prox->prox_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
- prox->prox_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
- prox->prox_dev->dev.parent = rmi4_data->pdev->dev.parent;
- input_set_drvdata(prox->prox_dev, rmi4_data);
-
- set_bit(EV_KEY, prox->prox_dev->evbit);
- set_bit(EV_ABS, prox->prox_dev->evbit);
- set_bit(BTN_TOUCH, prox->prox_dev->keybit);
- set_bit(BTN_TOOL_FINGER, prox->prox_dev->keybit);
-#ifdef INPUT_PROP_DIRECT
- set_bit(INPUT_PROP_DIRECT, prox->prox_dev->propbit);
-#endif
-
- prox_set_params();
-
- retval = input_register_device(prox->prox_dev);
- if (retval) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to register proximity device\n",
- __func__);
- goto exit_free_input_device;
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- goto exit_free_sysfs;
- }
- }
-
- return 0;
-
-exit_free_sysfs:
- for (attr_count--; attr_count >= 0; attr_count--) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- input_unregister_device(prox->prox_dev);
- prox->prox_dev = NULL;
-
-exit_free_input_device:
- if (prox->prox_dev)
- input_free_device(prox->prox_dev);
-
-exit_free_finger_data:
- kfree(prox->finger_data);
-
-exit_free_prox:
- kfree(prox);
- prox = NULL;
-
-exit:
- return retval;
-}
-
-static void synaptics_rmi4_prox_remove(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char attr_count;
-
- if (!prox)
- goto exit;
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- input_unregister_device(prox->prox_dev);
- kfree(prox->finger_data);
- kfree(prox);
- prox = NULL;
-
-exit:
- complete(&prox_remove_complete);
-
- return;
-}
-
-static void synaptics_rmi4_prox_reset(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!prox)
- return;
-
- prox_hover_finger_lift();
-
- prox_scan_pdt();
-
- prox_set_hover_finger_en();
-
- prox_set_params();
-
- return;
-}
-
-static void synaptics_rmi4_prox_reinit(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!prox)
- return;
-
- prox_hover_finger_lift();
-
- prox_set_hover_finger_en();
-
- return;
-}
-
-static void synaptics_rmi4_prox_e_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!prox)
- return;
-
- prox_hover_finger_lift();
-
- return;
-}
-
-static void synaptics_rmi4_prox_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!prox)
- return;
-
- prox_hover_finger_lift();
-
- return;
-}
-
-static struct synaptics_rmi4_exp_fn proximity_module = {
- .fn_type = RMI_PROXIMITY,
- .init = synaptics_rmi4_prox_init,
- .remove = synaptics_rmi4_prox_remove,
- .reset = synaptics_rmi4_prox_reset,
- .reinit = synaptics_rmi4_prox_reinit,
- .early_suspend = synaptics_rmi4_prox_e_suspend,
- .suspend = synaptics_rmi4_prox_suspend,
- .resume = NULL,
- .late_resume = NULL,
- .attn = synaptics_rmi4_prox_attn,
-};
-
-static int __init rmi4_proximity_module_init(void)
-{
- synaptics_rmi4_dsx_new_function(&proximity_module, true);
-
- return 0;
-}
-
-static void __exit rmi4_proximity_module_exit(void)
-{
- synaptics_rmi4_dsx_new_function(&proximity_module, false);
-
- wait_for_completion(&prox_remove_complete);
-
- return;
-}
-
-module_init(rmi4_proximity_module_init);
-module_exit(rmi4_proximity_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX Proximity Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
deleted file mode 100644
index c1cbec81d7d6..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
+++ /dev/null
@@ -1,810 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012 Synaptics Incorporated
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/gpio.h>
-#include <linux/uaccess.h>
-#include <linux/cdev.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2.h>
-#include "synaptics_dsx_core.h"
-
-#define CHAR_DEVICE_NAME "rmi"
-#define DEVICE_CLASS_NAME "rmidev"
-#define SYSFS_FOLDER_NAME "rmidev"
-#define DEV_NUMBER 1
-#define REG_ADDR_LIMIT 0xFFFF
-
-static ssize_t rmidev_sysfs_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t rmidev_sysfs_data_store(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t rmidev_sysfs_open_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t rmidev_sysfs_release_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-struct rmidev_handle {
- dev_t dev_no;
- struct device dev;
- struct synaptics_rmi4_data *rmi4_data;
- struct kobject *sysfs_dir;
- void *data;
- bool irq_enabled;
-};
-
-struct rmidev_data {
- int ref_count;
- struct cdev main_dev;
- struct class *device_class;
- struct mutex file_mutex;
- struct rmidev_handle *rmi_dev;
-};
-
-static struct bin_attribute attr_data = {
- .attr = {
- .name = "data",
- .mode = (S_IRUGO | S_IWUSR),
- },
- .size = 0,
- .read = rmidev_sysfs_data_show,
- .write = rmidev_sysfs_data_store,
-};
-
-static struct device_attribute attrs[] = {
- __ATTR(open, S_IWUSR | S_IWGRP,
- NULL,
- rmidev_sysfs_open_store),
- __ATTR(release, S_IWUSR | S_IWGRP,
- NULL,
- rmidev_sysfs_release_store),
- __ATTR(attn_state, S_IRUGO,
- rmidev_sysfs_attn_state_show,
- synaptics_rmi4_store_error),
-};
-
-static int rmidev_major_num;
-
-static struct class *rmidev_device_class;
-
-static struct rmidev_handle *rmidev;
-
-DECLARE_COMPLETION(rmidev_remove_complete);
-
-static irqreturn_t rmidev_sysfs_irq(int irq, void *data)
-{
- struct synaptics_rmi4_data *rmi4_data = data;
-
- sysfs_notify(&rmi4_data->input_dev->dev.kobj,
- SYSFS_FOLDER_NAME, "attn_state");
-
- return IRQ_HANDLED;
-}
-
-static int rmidev_sysfs_irq_enable(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
-{
- int retval = 0;
- unsigned char intr_status[MAX_INTR_REGISTERS];
- unsigned long irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
-
- if (enable) {
- if (rmidev->irq_enabled)
- return retval;
-
- /* Clear interrupts first */
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_data_base_addr + 1,
- intr_status,
- rmi4_data->num_of_intr_regs);
- if (retval < 0)
- return retval;
-
- retval = request_threaded_irq(rmi4_data->irq, NULL,
- rmidev_sysfs_irq, irq_flags,
- "synaptics_dsx_rmidev", rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create irq thread\n",
- __func__);
- return retval;
- }
-
- rmidev->irq_enabled = true;
- } else {
- if (rmidev->irq_enabled) {
- disable_irq(rmi4_data->irq);
- free_irq(rmi4_data->irq, rmi4_data);
- rmidev->irq_enabled = false;
- }
- }
-
- return retval;
-}
-
-static ssize_t rmidev_sysfs_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- unsigned int length = (unsigned int)count;
- unsigned short address = (unsigned short)pos;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (length > (REG_ADDR_LIMIT - address)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Out of register map limit\n",
- __func__);
- return -EINVAL;
- }
-
- if (length) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- address,
- (unsigned char *)buf,
- length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read data\n",
- __func__);
- return retval;
- }
- } else {
- return -EINVAL;
- }
-
- return length;
-}
-
-static ssize_t rmidev_sysfs_data_store(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- unsigned int length = (unsigned int)count;
- unsigned short address = (unsigned short)pos;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (length > (REG_ADDR_LIMIT - address)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Out of register map limit\n",
- __func__);
- return -EINVAL;
- }
-
- if (length) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- address,
- (unsigned char *)buf,
- length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write data\n",
- __func__);
- return retval;
- }
- } else {
- return -EINVAL;
- }
-
- return length;
-}
-
-static ssize_t rmidev_sysfs_open_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input != 1)
- return -EINVAL;
-
- rmi4_data->irq_enable(rmi4_data, false);
- rmidev_sysfs_irq_enable(rmi4_data, true);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt disabled\n",
- __func__);
-
- return count;
-}
-
-static ssize_t rmidev_sysfs_release_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input != 1)
- return -EINVAL;
-
- rmi4_data->reset_device(rmi4_data);
-
- rmidev_sysfs_irq_enable(rmi4_data, false);
- rmi4_data->irq_enable(rmi4_data, true);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt enabled\n",
- __func__);
-
- return count;
-}
-
-static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int attn_state;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- attn_state = gpio_get_value(bdata->irq_gpio);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", attn_state);
-}
-
-/*
- * rmidev_llseek - used to set up register address
- *
- * @filp: file structure for seek
- * @off: offset
- * if whence == SEEK_SET,
- * high 16 bits: page address
- * low 16 bits: register address
- * if whence == SEEK_CUR,
- * offset from current position
- * if whence == SEEK_END,
- * offset from end position (0xFFFF)
- * @whence: SEEK_SET, SEEK_CUR, or SEEK_END
- */
-static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
-{
- loff_t newpos;
- struct rmidev_data *dev_data = filp->private_data;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
-
- mutex_lock(&(dev_data->file_mutex));
-
- switch (whence) {
- case SEEK_SET:
- newpos = off;
- break;
- case SEEK_CUR:
- newpos = filp->f_pos + off;
- break;
- case SEEK_END:
- newpos = REG_ADDR_LIMIT + off;
- break;
- default:
- newpos = -EINVAL;
- goto clean_up;
- }
-
- if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: New position 0x%04x is invalid\n",
- __func__, (unsigned int)newpos);
- newpos = -EINVAL;
- goto clean_up;
- }
-
- filp->f_pos = newpos;
-
-clean_up:
- mutex_unlock(&(dev_data->file_mutex));
-
- return newpos;
-}
-
-/*
- * rmidev_read: - use to read data from rmi device
- *
- * @filp: file structure for read
- * @buf: user space buffer pointer
- * @count: number of bytes to read
- * @f_pos: offset (starting register address)
- */
-static ssize_t rmidev_read(struct file *filp, char __user *buf,
- size_t count, loff_t *f_pos)
-{
- ssize_t retval;
- unsigned char *tmpbuf;
- struct rmidev_data *dev_data = filp->private_data;
-
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
-
- mutex_lock(&(dev_data->file_mutex));
-
- if (count > (REG_ADDR_LIMIT - *f_pos))
- count = REG_ADDR_LIMIT - *f_pos;
-
- if (count == 0) {
- retval = 0;
- goto unlock;
- }
-
- if (*f_pos > REG_ADDR_LIMIT) {
- retval = -EFAULT;
- goto unlock;
- }
- tmpbuf = kzalloc(count + 1, GFP_KERNEL);
- if (!tmpbuf) {
- retval = -ENOMEM;
- goto unlock;
- }
- retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
- *f_pos,
- tmpbuf,
- count);
- if (retval < 0)
- goto clean_up;
-
- if (copy_to_user(buf, tmpbuf, count))
- retval = -EFAULT;
- else
- *f_pos += retval;
-
-clean_up:
- kfree(tmpbuf);
-unlock:
- mutex_unlock(&(dev_data->file_mutex));
- return retval;
-}
-
-/*
- * rmidev_write: - used to write data to rmi device
- *
- * @filep: file structure for write
- * @buf: user space buffer pointer
- * @count: number of bytes to write
- * @f_pos: offset (starting register address)
- */
-static ssize_t rmidev_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos)
-{
- ssize_t retval;
- unsigned char *tmpbuf;
- struct rmidev_data *dev_data = filp->private_data;
-
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
-
- mutex_lock(&(dev_data->file_mutex));
-
- if (*f_pos > REG_ADDR_LIMIT) {
- retval = -EFAULT;
- goto unlock;
- }
-
- if (count > (REG_ADDR_LIMIT - *f_pos))
- count = REG_ADDR_LIMIT - *f_pos;
-
- if (count == 0) {
- retval = 0;
- goto unlock;
- }
-
- tmpbuf = kzalloc(count + 1, GFP_KERNEL);
- if (!tmpbuf) {
- retval = -ENOMEM;
- goto unlock;
- }
-
- if (copy_from_user(tmpbuf, buf, count)) {
- retval = -EFAULT;
- goto clean_up;
- }
-
- retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
- *f_pos,
- tmpbuf,
- count);
- if (retval >= 0)
- *f_pos += retval;
-
-clean_up:
- kfree(tmpbuf);
-unlock:
- mutex_unlock(&(dev_data->file_mutex));
- return retval;
-}
-
-/*
- * rmidev_open: enable access to rmi device
- * @inp: inode struture
- * @filp: file structure
- */
-static int rmidev_open(struct inode *inp, struct file *filp)
-{
- int retval = 0;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- struct rmidev_data *dev_data =
- container_of(inp->i_cdev, struct rmidev_data, main_dev);
-
- if (!dev_data)
- return -EACCES;
-
- filp->private_data = dev_data;
-
- mutex_lock(&(dev_data->file_mutex));
-
- rmi4_data->irq_enable(rmi4_data, false);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt disabled\n",
- __func__);
-
- if (dev_data->ref_count < 1)
- dev_data->ref_count++;
- else
- retval = -EACCES;
-
- mutex_unlock(&(dev_data->file_mutex));
-
- return retval;
-}
-
-/*
- * rmidev_release: - release access to rmi device
- * @inp: inode structure
- * @filp: file structure
- */
-static int rmidev_release(struct inode *inp, struct file *filp)
-{
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- struct rmidev_data *dev_data =
- container_of(inp->i_cdev, struct rmidev_data, main_dev);
-
- if (!dev_data)
- return -EACCES;
-
- rmi4_data->reset_device(rmi4_data);
-
- mutex_lock(&(dev_data->file_mutex));
-
- dev_data->ref_count--;
- if (dev_data->ref_count < 0)
- dev_data->ref_count = 0;
-
- rmi4_data->irq_enable(rmi4_data, true);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt enabled\n",
- __func__);
-
- mutex_unlock(&(dev_data->file_mutex));
-
- return 0;
-}
-
-static const struct file_operations rmidev_fops = {
- .owner = THIS_MODULE,
- .llseek = rmidev_llseek,
- .read = rmidev_read,
- .write = rmidev_write,
- .open = rmidev_open,
- .release = rmidev_release,
-};
-
-static void rmidev_device_cleanup(struct rmidev_data *dev_data)
-{
- dev_t devno;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (dev_data) {
- devno = dev_data->main_dev.dev;
-
- if (dev_data->device_class)
- device_destroy(dev_data->device_class, devno);
-
- cdev_del(&dev_data->main_dev);
-
- unregister_chrdev_region(devno, 1);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: rmidev device removed\n",
- __func__);
- }
-
- return;
-}
-
-static char *rmi_char_devnode(struct device *dev, umode_t *mode)
-{
- if (!mode)
- return NULL;
-
- *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
-
- return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
-}
-
-static int rmidev_create_device_class(void)
-{
- rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
-
- if (IS_ERR(rmidev_device_class)) {
- pr_err("%s: Failed to create /dev/%s\n",
- __func__, CHAR_DEVICE_NAME);
- return -ENODEV;
- }
-
- rmidev_device_class->devnode = rmi_char_devnode;
-
- return 0;
-}
-
-static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- dev_t dev_no;
- unsigned char attr_count;
- struct rmidev_data *dev_data;
- struct device *device_ptr;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL);
- if (!rmidev) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for rmidev\n",
- __func__);
- retval = -ENOMEM;
- goto err_rmidev;
- }
-
- rmidev->rmi4_data = rmi4_data;
-
- retval = rmidev_create_device_class();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create device class\n",
- __func__);
- goto err_device_class;
- }
-
- if (rmidev_major_num) {
- dev_no = MKDEV(rmidev_major_num, DEV_NUMBER);
- retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
- } else {
- retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate char device region\n",
- __func__);
- goto err_device_region;
- }
-
- rmidev_major_num = MAJOR(dev_no);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Major number of rmidev = %d\n",
- __func__, rmidev_major_num);
- }
-
- dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
- if (!dev_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for dev_data\n",
- __func__);
- retval = -ENOMEM;
- goto err_dev_data;
- }
-
- mutex_init(&dev_data->file_mutex);
- dev_data->rmi_dev = rmidev;
- rmidev->data = dev_data;
-
- cdev_init(&dev_data->main_dev, &rmidev_fops);
-
- retval = cdev_add(&dev_data->main_dev, dev_no, 1);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to add rmi char device\n",
- __func__);
- goto err_char_device;
- }
-
- dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no));
- dev_data->device_class = rmidev_device_class;
-
- device_ptr = device_create(dev_data->device_class, NULL, dev_no,
- NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no));
- if (IS_ERR(device_ptr)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create rmi char device\n",
- __func__);
- retval = -ENODEV;
- goto err_char_device;
- }
-
- retval = gpio_export(bdata->irq_gpio, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to export attention gpio\n",
- __func__);
- } else {
- retval = gpio_export_link(&(rmi4_data->input_dev->dev),
- "attn", bdata->irq_gpio);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s Failed to create gpio symlink\n",
- __func__);
- } else {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Exported attention gpio %d\n",
- __func__, bdata->irq_gpio);
- }
- }
-
- rmidev->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME,
- &rmi4_data->input_dev->dev.kobj);
- if (!rmidev->sysfs_dir) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs directory\n",
- __func__);
- retval = -ENODEV;
- goto err_sysfs_dir;
- }
-
- retval = sysfs_create_bin_file(rmidev->sysfs_dir,
- &attr_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs bin file\n",
- __func__);
- goto err_sysfs_bin;
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(rmidev->sysfs_dir,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- retval = -ENODEV;
- goto err_sysfs_attrs;
- }
- }
-
- return 0;
-
-err_sysfs_attrs:
- for (attr_count--; attr_count >= 0; attr_count--)
- sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
-
- sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
-
-err_sysfs_bin:
- kobject_put(rmidev->sysfs_dir);
-
-err_sysfs_dir:
-err_char_device:
- rmidev_device_cleanup(dev_data);
- kfree(dev_data);
-
-err_dev_data:
- unregister_chrdev_region(dev_no, 1);
-
-err_device_region:
- class_destroy(rmidev_device_class);
-
-err_device_class:
- kfree(rmidev);
- rmidev = NULL;
-
-err_rmidev:
- return retval;
-}
-
-static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char attr_count;
- struct rmidev_data *dev_data;
-
- if (!rmidev)
- goto exit;
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
- sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
-
- sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
-
- kobject_put(rmidev->sysfs_dir);
-
- dev_data = rmidev->data;
- if (dev_data) {
- rmidev_device_cleanup(dev_data);
- kfree(dev_data);
- }
-
- unregister_chrdev_region(rmidev->dev_no, 1);
-
- class_destroy(rmidev_device_class);
-
- kfree(rmidev);
- rmidev = NULL;
-
-exit:
- complete(&rmidev_remove_complete);
-
- return;
-}
-
-static struct synaptics_rmi4_exp_fn rmidev_module = {
- .fn_type = RMI_DEV,
- .init = rmidev_init_device,
- .remove = rmidev_remove_device,
- .reset = NULL,
- .reinit = NULL,
- .early_suspend = NULL,
- .suspend = NULL,
- .resume = NULL,
- .late_resume = NULL,
- .attn = NULL,
-};
-
-static int __init rmidev_module_init(void)
-{
- synaptics_rmi4_dsx_new_function(&rmidev_module, true);
-
- return 0;
-}
-
-static void __exit rmidev_module_exit(void)
-{
- synaptics_rmi4_dsx_new_function(&rmidev_module, false);
-
- wait_for_completion(&rmidev_remove_complete);
-
- return;
-}
-
-module_init(rmidev_module_init);
-module_exit(rmidev_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 065379c1397f..c6f74b149706 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -367,6 +367,8 @@ struct arm_smmu_device {
u32 num_mapping_groups;
DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
+ u32 ubs;
+
unsigned long va_size;
unsigned long ipa_size;
unsigned long pa_size;
@@ -404,6 +406,7 @@ struct arm_smmu_device {
struct msm_bus_scale_pdata *bus_pdata;
enum tz_smmu_device_id sec_id;
+ int regulator_defer;
};
struct arm_smmu_cfg {
@@ -950,7 +953,8 @@ static int arm_smmu_disable_regulators(struct arm_smmu_device *smmu)
arm_smmu_unprepare_clocks(smmu);
arm_smmu_unrequest_bus(smmu);
if (smmu->gdsc) {
- ret = regulator_disable(smmu->gdsc);
+ ret = regulator_disable_deferred(smmu->gdsc,
+ smmu->regulator_defer);
WARN(ret, "%s: Regulator disable failed\n",
dev_name(smmu->dev));
}
@@ -1754,6 +1758,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
{
int irq, start, ret = 0;
unsigned long ias, oas;
+ int sep = 0;
struct io_pgtable_ops *pgtbl_ops;
enum io_pgtable_fmt fmt;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
@@ -1795,9 +1800,27 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
start = smmu->num_s2_context_banks;
ias = smmu->va_size;
oas = smmu->ipa_size;
- if (IS_ENABLED(CONFIG_64BIT))
+ if (IS_ENABLED(CONFIG_64BIT)) {
fmt = ARM_64_LPAE_S1;
- else
+
+ if (quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) {
+
+ /*
+ * When the UBS id is 5 we know that the bus
+ * size is 49 bits and that bit 48 is the fixed
+ * sign extension bit. For any other bus size
+ * we need to specify the sign extension bit
+ * and adjust the input size accordingly
+ */
+
+ if (smmu->ubs == 5) {
+ sep = 48;
+ } else {
+ sep = ias - 1;
+ ias--;
+ }
+ }
+ } else
fmt = ARM_32_LPAE_S1;
break;
case ARM_SMMU_DOMAIN_NESTED:
@@ -1859,6 +1882,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
.pgsize_bitmap = arm_smmu_ops.pgsize_bitmap,
.ias = ias,
.oas = oas,
+ .sep = sep,
.tlb = &arm_smmu_gather_ops,
.iommu_dev = smmu->dev,
.iova_base = domain->geometry.aperture_start,
@@ -3610,6 +3634,12 @@ static int arm_smmu_init_regulators(struct arm_smmu_device *smmu)
if (!of_get_property(dev->of_node, "vdd-supply", NULL))
return 0;
+ if (!of_property_read_u32(dev->of_node,
+ "qcom,deferred-regulator-disable-delay",
+ &(smmu->regulator_defer)))
+ dev_info(dev, "regulator defer delay %d\n",
+ smmu->regulator_defer);
+
smmu->gdsc = devm_regulator_get(dev, "vdd");
if (IS_ERR(smmu->gdsc))
return PTR_ERR(smmu->gdsc);
@@ -3888,12 +3918,13 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
smmu->va_size = smmu->ipa_size;
size = SZ_4K | SZ_2M | SZ_1G;
} else {
- size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
- smmu->va_size = arm_smmu_id_size_to_bits(size);
+ smmu->ubs = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
+
+ smmu->va_size = arm_smmu_id_size_to_bits(smmu->ubs);
#ifndef CONFIG_64BIT
smmu->va_size = min(32UL, smmu->va_size);
#endif
- smmu->va_size = min(36UL, smmu->va_size);
+ smmu->va_size = min(39UL, smmu->va_size);
size = 0;
if (id & ID2_PTFS_4K)
size |= SZ_4K | SZ_2M | SZ_1G;
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/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 2d2583c78bdb..7651545e3f2e 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -550,9 +550,18 @@ static inline arm_lpae_iopte *arm_lpae_get_table(
{
struct io_pgtable_cfg *cfg = &data->iop.cfg;
- return ((cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) &&
- (iova & (1UL << (cfg->ias - 1)))) ?
- data->pgd[1] : data->pgd[0];
+ /*
+ * iovas for TTBR1 will have all the bits set between the input address
+ * region and the sign extension bit
+ */
+ if (unlikely(cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1)) {
+ unsigned long mask = GENMASK(cfg->sep, cfg->ias);
+
+ if ((iova & mask) == mask)
+ return data->pgd[1];
+ }
+
+ return data->pgd[0];
}
static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
@@ -1089,26 +1098,26 @@ static u64 arm64_lpae_setup_ttbr1(struct io_pgtable_cfg *cfg,
/* Set T1SZ */
reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T1SZ_SHIFT;
- /* Set the SEP bit based on the size */
- switch (cfg->ias) {
- case 32:
+ switch (cfg->sep) {
+ case 31:
reg |= (ARM_LPAE_TCR_SEP_31 << ARM_LPAE_TCR_SEP_SHIFT);
break;
- case 36:
+ case 35:
reg |= (ARM_LPAE_TCR_SEP_35 << ARM_LPAE_TCR_SEP_SHIFT);
break;
- case 40:
+ case 39:
reg |= (ARM_LPAE_TCR_SEP_39 << ARM_LPAE_TCR_SEP_SHIFT);
break;
- case 42:
+ case 41:
reg |= (ARM_LPAE_TCR_SEP_41 << ARM_LPAE_TCR_SEP_SHIFT);
break;
- case 44:
+ case 43:
reg |= (ARM_LPAE_TCR_SEP_43 << ARM_LPAE_TCR_SEP_SHIFT);
break;
- case 48:
+ case 47:
reg |= (ARM_LPAE_TCR_SEP_47 << ARM_LPAE_TCR_SEP_SHIFT);
break;
+ case 48:
default:
reg |= (ARM_LPAE_TCR_SEP_UPSTREAM << ARM_LPAE_TCR_SEP_SHIFT);
break;
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index 2cf213514221..0326bb6a4afa 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -67,6 +67,7 @@ struct io_pgtable_cfg {
unsigned long pgsize_bitmap;
unsigned int ias;
unsigned int oas;
+ int sep;
const struct iommu_gather_ops *tlb;
struct device *iommu_dev;
dma_addr_t iova_base;
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index b8155c9227d3..c21846c1f8d5 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -1491,6 +1491,10 @@ static ssize_t iommu_debug_pte_read(struct file *file, char __user *ubuf,
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!dev->archdata.mapping) {
pr_err("No mapping. Did you already attach?\n");
return -EINVAL;
@@ -1610,6 +1614,10 @@ static ssize_t iommu_debug_dma_atos_read(struct file *file, char __user *ubuf,
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!dev->archdata.mapping) {
pr_err("No mapping. Did you already attach?\n");
return -EINVAL;
diff --git a/drivers/irqchip/irq-xtensa-mx.c b/drivers/irqchip/irq-xtensa-mx.c
index bb3ac5fe5846..72a391e01011 100644
--- a/drivers/irqchip/irq-xtensa-mx.c
+++ b/drivers/irqchip/irq-xtensa-mx.c
@@ -142,7 +142,7 @@ static struct irq_chip xtensa_mx_irq_chip = {
int __init xtensa_mx_init_legacy(struct device_node *interrupt_parent)
{
struct irq_domain *root_domain =
- irq_domain_add_legacy(NULL, NR_IRQS, 0, 0,
+ irq_domain_add_legacy(NULL, NR_IRQS - 1, 1, 0,
&xtensa_mx_irq_domain_ops,
&xtensa_mx_irq_chip);
irq_set_default_host(root_domain);
diff --git a/drivers/irqchip/irq-xtensa-pic.c b/drivers/irqchip/irq-xtensa-pic.c
index 472ae1770964..f728755fa292 100644
--- a/drivers/irqchip/irq-xtensa-pic.c
+++ b/drivers/irqchip/irq-xtensa-pic.c
@@ -89,7 +89,7 @@ static struct irq_chip xtensa_irq_chip = {
int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent)
{
struct irq_domain *root_domain =
- irq_domain_add_legacy(NULL, NR_IRQS, 0, 0,
+ irq_domain_add_legacy(NULL, NR_IRQS - 1, 1, 0,
&xtensa_irq_domain_ops, &xtensa_irq_chip);
irq_set_default_host(root_domain);
return 0;
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index a89a92d253ac..564d20079715 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -721,7 +721,8 @@ static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led)
#define VIN_FLASH_MIN_UV 3300000LL
static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led)
{
- int ocv_uv, rbatt_uohm, ibat_now, voltage_hdrm_mv, rc;
+ int ocv_uv = 0, rbatt_uohm = 0, ibat_now = 0, voltage_hdrm_mv = 0;
+ int rc = 0;
int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip;
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-android-verity.c b/drivers/md/dm-android-verity.c
index 7cef735a01a7..4f6086970131 100644
--- a/drivers/md/dm-android-verity.c
+++ b/drivers/md/dm-android-verity.c
@@ -691,7 +691,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
dev_t uninitialized_var(dev);
struct android_metadata *metadata = NULL;
int err = 0, i, mode;
- char *key_id, *table_ptr, dummy, *target_device,
+ char *key_id = NULL, *table_ptr, dummy, *target_device,
*verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS];
/* One for specifying number of opt args and one for mode */
sector_t data_sectors;
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/Kconfig b/drivers/media/Kconfig
index 0b219a81e8a2..c1b999c6cff2 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -99,6 +99,10 @@ config MEDIA_CEC_DEBUG
config MEDIA_CEC_EDID
bool
+config MEDIA_CEC_NOTIFIER
+ bool
+ select MEDIA_CEC_EDID
+
#
# Media controller
# Selectable only for webcam/grabbers, as other drivers don't use it
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index ba516dcbc6aa..6c7801919b98 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -10,6 +10,10 @@ ifeq ($(CONFIG_MEDIA_CEC_SUPPORT),y)
obj-$(CONFIG_MEDIA_SUPPORT) += cec/
endif
+ifeq ($(CONFIG_MEDIA_CEC_NOTIFIER),y)
+ obj-$(CONFIG_MEDIA_SUPPORT) += cec-notifier.o
+endif
+
media-objs := media-device.o media-devnode.o media-entity.o
#
diff --git a/drivers/media/cec-notifier.c b/drivers/media/cec-notifier.c
new file mode 100644
index 000000000000..5f5209a73665
--- /dev/null
+++ b/drivers/media/cec-notifier.c
@@ -0,0 +1,129 @@
+/*
+ * cec-notifier.c - notify CEC drivers of physical address changes
+ *
+ * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk>
+ * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/kref.h>
+
+#include <media/cec-notifier.h>
+#include <drm/drm_edid.h>
+
+struct cec_notifier {
+ struct mutex lock;
+ struct list_head head;
+ struct kref kref;
+ struct device *dev;
+ struct cec_adapter *cec_adap;
+ void (*callback)(struct cec_adapter *adap, u16 pa);
+
+ u16 phys_addr;
+};
+
+static LIST_HEAD(cec_notifiers);
+static DEFINE_MUTEX(cec_notifiers_lock);
+
+struct cec_notifier *cec_notifier_get(struct device *dev)
+{
+ struct cec_notifier *n;
+
+ mutex_lock(&cec_notifiers_lock);
+ list_for_each_entry(n, &cec_notifiers, head) {
+ if (n->dev == dev) {
+ kref_get(&n->kref);
+ mutex_unlock(&cec_notifiers_lock);
+ return n;
+ }
+ }
+ n = kzalloc(sizeof(*n), GFP_KERNEL);
+ if (!n)
+ goto unlock;
+ n->dev = dev;
+ n->phys_addr = CEC_PHYS_ADDR_INVALID;
+ mutex_init(&n->lock);
+ kref_init(&n->kref);
+ list_add_tail(&n->head, &cec_notifiers);
+unlock:
+ mutex_unlock(&cec_notifiers_lock);
+ return n;
+}
+EXPORT_SYMBOL_GPL(cec_notifier_get);
+
+static void cec_notifier_release(struct kref *kref)
+{
+ struct cec_notifier *n =
+ container_of(kref, struct cec_notifier, kref);
+
+ list_del(&n->head);
+ kfree(n);
+}
+
+void cec_notifier_put(struct cec_notifier *n)
+{
+ mutex_lock(&cec_notifiers_lock);
+ kref_put(&n->kref, cec_notifier_release);
+ mutex_unlock(&cec_notifiers_lock);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_put);
+
+void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
+{
+ mutex_lock(&n->lock);
+ n->phys_addr = pa;
+ if (n->callback)
+ n->callback(n->cec_adap, n->phys_addr);
+ mutex_unlock(&n->lock);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
+
+void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
+ const struct edid *edid)
+{
+ u16 pa = CEC_PHYS_ADDR_INVALID;
+
+ if (edid && edid->extensions)
+ pa = cec_get_edid_phys_addr((const u8 *)edid,
+ EDID_LENGTH * (edid->extensions + 1), NULL);
+ cec_notifier_set_phys_addr(n, pa);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
+
+void cec_notifier_register(struct cec_notifier *n,
+ struct cec_adapter *adap,
+ void (*callback)(struct cec_adapter *adap, u16 pa))
+{
+ kref_get(&n->kref);
+ mutex_lock(&n->lock);
+ n->cec_adap = adap;
+ n->callback = callback;
+ n->callback(adap, n->phys_addr);
+ mutex_unlock(&n->lock);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_register);
+
+void cec_notifier_unregister(struct cec_notifier *n)
+{
+ mutex_lock(&n->lock);
+ n->callback = NULL;
+ mutex_unlock(&n->lock);
+ cec_notifier_put(n);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_unregister);
diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c
index aca3ab83a8a1..61adc28ec8ec 100644
--- a/drivers/media/cec/cec-core.c
+++ b/drivers/media/cec/cec-core.c
@@ -195,6 +195,24 @@ static void cec_devnode_unregister(struct cec_devnode *devnode)
put_device(&devnode->dev);
}
+#ifdef CONFIG_MEDIA_CEC_NOTIFIER
+static void cec_cec_notify(struct cec_adapter *adap, u16 pa)
+{
+ cec_s_phys_addr(adap, pa, false);
+}
+
+void cec_register_cec_notifier(struct cec_adapter *adap,
+ struct cec_notifier *notifier)
+{
+ if (WARN_ON(!adap->devnode.registered))
+ return;
+
+ adap->notifier = notifier;
+ cec_notifier_register(adap->notifier, adap, cec_cec_notify);
+}
+EXPORT_SYMBOL_GPL(cec_register_cec_notifier);
+#endif
+
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
void *priv, const char *name, u32 caps,
u8 available_las)
@@ -344,6 +362,10 @@ void cec_unregister_adapter(struct cec_adapter *adap)
adap->rc = NULL;
#endif
debugfs_remove_recursive(adap->cec_dir);
+#ifdef CONFIG_MEDIA_CEC_NOTIFIER
+ if (adap->notifier)
+ cec_notifier_unregister(adap->notifier);
+#endif
cec_devnode_unregister(&adap->devnode);
}
EXPORT_SYMBOL_GPL(cec_unregister_adapter);
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/ais/camera/camera.c b/drivers/media/platform/msm/ais/camera/camera.c
index 158b83c12d00..33808d18d4c4 100644
--- a/drivers/media/platform/msm/ais/camera/camera.c
+++ b/drivers/media/platform/msm/ais/camera/camera.c
@@ -491,13 +491,16 @@ static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh,
if (WARN_ON(!k_ioctl || !pvdev))
return -EIO;
+ if (cmd != VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD)
+ return -EINVAL;
+
switch (k_ioctl->id) {
case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: {
struct msm_camera_return_buf ptr, *tmp = NULL;
MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl->ioctl_ptr,
sizeof(tmp));
- if (copy_from_user(&ptr, tmp,
+ if (copy_from_user(&ptr, (void __user *)tmp,
sizeof(struct msm_camera_return_buf))) {
return -EFAULT;
}
@@ -795,7 +798,7 @@ static long camera_handle_internal_compat_ioctl(struct file *file,
{
long rc = 0;
struct msm_camera_private_ioctl_arg k_ioctl;
- void __user *tmp_compat_ioctl_ptr = NULL;
+ void *tmp_compat_ioctl_ptr = NULL;
rc = msm_copy_camera_private_ioctl_args(arg,
&k_ioctl, &tmp_compat_ioctl_ptr);
@@ -810,11 +813,13 @@ static long camera_handle_internal_compat_ioctl(struct file *file,
k_ioctl.id, k_ioctl.size);
return -EINVAL;
}
- k_ioctl.ioctl_ptr = (__u64)tmp_compat_ioctl_ptr;
- if (!k_ioctl.ioctl_ptr) {
+
+ if (tmp_compat_ioctl_ptr == NULL) {
pr_debug("Invalid ptr for id %d", k_ioctl.id);
return -EINVAL;
}
+ k_ioctl.ioctl_ptr = (__u64)(uintptr_t)tmp_compat_ioctl_ptr;
+
rc = camera_v4l2_vidioc_private_ioctl(file, file->private_data,
0, cmd, (void *)&k_ioctl);
}
@@ -826,7 +831,7 @@ static long camera_handle_internal_compat_ioctl(struct file *file,
return rc;
}
-long camera_v4l2_compat_ioctl(struct file *file, unsigned int cmd,
+static long camera_v4l2_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long ret = 0;
diff --git a/drivers/media/platform/msm/ais/common/cam_hw_ops.c b/drivers/media/platform/msm/ais/common/cam_hw_ops.c
index 073778c9edcc..cf28e0ca6536 100644
--- a/drivers/media/platform/msm/ais/common/cam_hw_ops.c
+++ b/drivers/media/platform/msm/ais/common/cam_hw_ops.c
@@ -50,7 +50,7 @@ struct cam_ahb_client_data {
static struct cam_ahb_client_data data;
-int get_vector_index(char *name)
+static int get_vector_index(char *name)
{
int i = 0, rc = -1;
@@ -213,7 +213,7 @@ err1:
}
EXPORT_SYMBOL(cam_ahb_clk_init);
-int cam_consolidate_ahb_vote(enum cam_ahb_clk_client id,
+static int cam_consolidate_ahb_vote(enum cam_ahb_clk_client id,
enum cam_ahb_clk_vote vote)
{
int i = 0;
diff --git a/drivers/media/platform/msm/ais/common/cam_smmu_api.c b/drivers/media/platform/msm/ais/common/cam_smmu_api.c
index d3b239e9f304..0f0e14506325 100644
--- a/drivers/media/platform/msm/ais/common/cam_smmu_api.c
+++ b/drivers/media/platform/msm/ais/common/cam_smmu_api.c
@@ -466,7 +466,7 @@ static enum dma_data_direction cam_smmu_translate_dir(
return DMA_NONE;
}
-void cam_smmu_reset_iommu_table(enum cam_smmu_init_dir ops)
+static void cam_smmu_reset_iommu_table(enum cam_smmu_init_dir ops)
{
unsigned int i;
int j = 0;
diff --git a/drivers/media/platform/msm/ais/common/cam_smmu_api.h b/drivers/media/platform/msm/ais/common/cam_smmu_api.h
index 4a13598dc719..26bd30a6c8c8 100644
--- a/drivers/media/platform/msm/ais/common/cam_smmu_api.h
+++ b/drivers/media/platform/msm/ais/common/cam_smmu_api.h
@@ -43,6 +43,10 @@ enum cam_smmu_map_dir {
CAM_SMMU_MAP_INVALID
};
+int cam_smmu_query_vaddr_in_range(int handle,
+ unsigned long fault_addr, unsigned long *start_addr,
+ unsigned long *end_addr, int *fd);
+
/**
* @param identifier: Unique identifier to be used by clients which they
* should get from device tree. CAM SMMU driver will
diff --git a/drivers/media/platform/msm/ais/common/cam_soc_api.c b/drivers/media/platform/msm/ais/common/cam_soc_api.c
index 118d665a44d3..92f3e4007390 100644
--- a/drivers/media/platform/msm/ais/common/cam_soc_api.c
+++ b/drivers/media/platform/msm/ais/common/cam_soc_api.c
@@ -36,7 +36,7 @@ struct msm_cam_bus_pscale_data {
struct mutex lock;
};
-struct msm_cam_bus_pscale_data g_cv[CAM_BUS_CLIENT_MAX];
+static struct msm_cam_bus_pscale_data g_cv[CAM_BUS_CLIENT_MAX];
/* Get all clocks from DT */
static int msm_camera_get_clk_info_internal(struct device *dev,
@@ -771,7 +771,7 @@ void __iomem *msm_camera_get_reg_base(struct platform_device *pdev,
char *device_name, int reserve_mem)
{
struct resource *mem;
- void *base;
+ void __iomem *base;
if (!pdev || !device_name) {
pr_err("Invalid params\n");
diff --git a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c
index 8370f556a40d..22518c2cae7d 100644
--- a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c
+++ b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c
@@ -123,8 +123,8 @@ void msm_camera_io_memcpy_toio(void __iomem *dest_addr,
void __iomem *src_addr, u32 len)
{
int i;
- u32 *d = (u32 *) dest_addr;
- u32 *s = (u32 *) src_addr;
+ u32 __iomem *d = (u32 __iomem *) dest_addr;
+ u32 __iomem *s = (u32 __iomem *) src_addr;
for (i = 0; i < len; i++)
writel_relaxed(*s++, d++);
@@ -178,7 +178,7 @@ void msm_camera_io_dump(void __iomem *addr, int size, int enable)
{
char line_str[128], *p_str;
int i;
- u32 *p = (u32 *) addr;
+ u32 __iomem *p = (u32 __iomem *) addr;
u32 data;
CDBG("%s: addr=%pK size=%d\n", __func__, addr, size);
@@ -242,8 +242,8 @@ void msm_camera_io_memcpy_mb(void __iomem *dest_addr,
void __iomem *src_addr, u32 len)
{
int i;
- u32 *d = (u32 *) dest_addr;
- u32 *s = (u32 *) src_addr;
+ u32 __iomem *d = (u32 __iomem *) dest_addr;
+ u32 __iomem *s = (u32 __iomem *) src_addr;
/* This is generic function called who needs to register
* writes with memory barrier
*/
diff --git a/drivers/media/platform/msm/ais/common/msm_camera_io_util.h b/drivers/media/platform/msm/ais/common/msm_camera_io_util.h
index 338e24d45500..3bd6c5f4866e 100644
--- a/drivers/media/platform/msm/ais/common/msm_camera_io_util.h
+++ b/drivers/media/platform/msm/ais/common/msm_camera_io_util.h
@@ -40,6 +40,8 @@ void msm_camera_io_w(u32 data, void __iomem *addr);
void msm_camera_io_w_mb(u32 data, void __iomem *addr);
u32 msm_camera_io_r(void __iomem *addr);
u32 msm_camera_io_r_mb(void __iomem *addr);
+void msm_camera_io_memcpy_toio(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len);
void msm_camera_io_dump(void __iomem *addr, int size, int enable);
void msm_camera_io_memcpy(void __iomem *dest_addr,
void __iomem *src_addr, u32 len);
diff --git a/drivers/media/platform/msm/ais/fd/msm_fd_dev.c b/drivers/media/platform/msm/ais/fd/msm_fd_dev.c
index 7a4acf6ec815..420083f019cf 100644
--- a/drivers/media/platform/msm/ais/fd/msm_fd_dev.c
+++ b/drivers/media/platform/msm/ais/fd/msm_fd_dev.c
@@ -745,9 +745,13 @@ static int msm_fd_s_fmt_vid_out(struct file *file,
static int msm_fd_reqbufs(struct file *file,
void *fh, struct v4l2_requestbuffers *req)
{
+ int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- return vb2_reqbufs(&ctx->vb2_q, req);
+ mutex_lock(&ctx->fd_device->recovery_lock);
+ ret = vb2_reqbufs(&ctx->vb2_q, req);
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+ return ret;
}
/*
@@ -759,9 +763,14 @@ static int msm_fd_reqbufs(struct file *file,
static int msm_fd_qbuf(struct file *file, void *fh,
struct v4l2_buffer *pb)
{
+ int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- return vb2_qbuf(&ctx->vb2_q, pb);
+ mutex_lock(&ctx->fd_device->recovery_lock);
+ ret = vb2_qbuf(&ctx->vb2_q, pb);
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+ return ret;
+
}
/*
@@ -773,9 +782,13 @@ static int msm_fd_qbuf(struct file *file, void *fh,
static int msm_fd_dqbuf(struct file *file,
void *fh, struct v4l2_buffer *pb)
{
+ int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- return vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK);
+ mutex_lock(&ctx->fd_device->recovery_lock);
+ ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK);
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+ return ret;
}
/*
diff --git a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c
index fcd803d4b138..585865b12387 100644
--- a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c
@@ -76,7 +76,7 @@ static int msm_buf_check_head_sanity(struct msm_isp_bufq *bufq)
return rc;
}
-struct msm_isp_bufq *msm_isp_get_bufq(
+static struct msm_isp_bufq *msm_isp_get_bufq(
struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle)
{
@@ -161,7 +161,7 @@ static int msm_isp_free_bufq_handle(struct msm_isp_buf_mgr *buf_mgr,
/* Set everything except lock to 0 */
bufq->bufq_handle = 0;
- bufq->bufs = 0;
+ bufq->bufs = NULL;
bufq->vfe_id = 0;
bufq->output_id = 0;
bufq->num_bufs = 0;
@@ -212,8 +212,8 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
mapped_info->paddr += accu_length;
accu_length += qbuf_buf->planes[i].length;
- CDBG("%s: plane: %d addr:%lu\n",
- __func__, i, (unsigned long)mapped_info->paddr);
+ CDBG("%s: plane: %d addr:%pK\n",
+ __func__, i, (void *)mapped_info->paddr);
}
buf_info->num_planes = qbuf_buf->num_planes;
@@ -275,8 +275,8 @@ static int msm_isp_map_buf(struct msm_isp_buf_mgr *buf_mgr,
pr_err_ratelimited("%s: cannot map address", __func__);
goto smmu_map_error;
}
- CDBG("%s: addr:%lu\n",
- __func__, (unsigned long)mapped_info->paddr);
+ CDBG("%s: addr:%pK\n",
+ __func__, (void *)mapped_info->paddr);
return rc;
smmu_map_error:
@@ -1367,14 +1367,15 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
struct msm_isp_buffer *bufs = NULL;
uint32_t i = 0, j = 0, k = 0, rc = 0;
char *print_buf = NULL, temp_buf[100];
- uint32_t start_addr = 0, end_addr = 0, print_buf_size = 2000;
+ uint32_t print_buf_size = 2000;
+ unsigned long start_addr = 0, end_addr = 0;
int buf_addr_delta = -1;
int temp_delta = 0;
uint32_t debug_output_id = 0;
uint32_t debug_buf_idx = 0;
uint32_t debug_buf_plane = 0;
- uint32_t debug_start_addr = 0;
- uint32_t debug_end_addr = 0;
+ unsigned long debug_start_addr = 0;
+ unsigned long debug_end_addr = 0;
uint32_t debug_frame_id = 0;
enum msm_isp_buffer_state debug_state = MSM_ISP_BUFFER_STATE_UNUSED;
unsigned long flags;
@@ -1433,8 +1434,8 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
debug_output_id, debug_frame_id);
pr_err("%s: nearby buf index %d, plane %d, state %d\n", __func__,
debug_buf_idx, debug_buf_plane, debug_state);
- pr_err("%s: buf address 0x%x -- 0x%x\n", __func__,
- debug_start_addr, debug_end_addr);
+ pr_err("%s: buf address %pK -- %pK\n", __func__,
+ (void *)debug_start_addr, (void *)debug_end_addr);
if (BUF_DEBUG_FULL) {
print_buf = kzalloc(print_buf_size, GFP_ATOMIC);
@@ -1469,9 +1470,10 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
mapped_info[k].len;
snprintf(temp_buf,
sizeof(temp_buf),
- " buf %d plane %d start_addr %x end_addr %x\n",
- j, k, start_addr,
- end_addr);
+ " buf %d plane %d start_addr %pK end_addr %pK\n",
+ j, k,
+ (void *)start_addr,
+ (void *)end_addr);
strlcat(print_buf, temp_buf,
print_buf_size);
}
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp.c b/drivers/media/platform/msm/ais/isp/msm_isp.c
index 97c0f779cf73..d62b830535a3 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp.c
@@ -49,9 +49,6 @@ MODULE_DEVICE_TABLE(of, msm_vfe_dt_match);
#define OVERFLOW_BUFFER_LENGTH 64
static char stat_line[OVERFLOW_LENGTH];
-struct msm_isp_statistics stats;
-struct msm_isp_ub_info ub_info;
-
static int msm_isp_enable_debugfs(struct vfe_device *vfe_dev,
struct msm_isp_bw_req_info *isp_req_hist);
@@ -107,8 +104,8 @@ static int vfe_debugfs_statistics_open(struct inode *inode, struct file *file)
return 0;
}
-static ssize_t vfe_debugfs_statistics_read(struct file *t_file, char *t_char,
- size_t t_size_t, loff_t *t_loff_t)
+static ssize_t vfe_debugfs_statistics_read(struct file *t_file,
+ char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
{
int i;
uint64_t *ptr;
@@ -132,7 +129,7 @@ static ssize_t vfe_debugfs_statistics_read(struct file *t_file, char *t_char,
}
static ssize_t vfe_debugfs_statistics_write(struct file *t_file,
- const char *t_char, size_t t_size_t, loff_t *t_loff_t)
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
{
struct vfe_device *vfe_dev = (struct vfe_device *)
t_file->private_data;
@@ -149,7 +146,7 @@ static int bw_history_open(struct inode *inode, struct file *file)
return 0;
}
-static ssize_t bw_history_read(struct file *t_file, char *t_char,
+static ssize_t bw_history_read(struct file *t_file, char __user *t_char,
size_t t_size_t, loff_t *t_loff_t)
{
int i;
@@ -194,7 +191,7 @@ static ssize_t bw_history_read(struct file *t_file, char *t_char,
}
static ssize_t bw_history_write(struct file *t_file,
- const char *t_char, size_t t_size_t, loff_t *t_loff_t)
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
{
struct msm_isp_bw_req_info *isp_req_hist =
(struct msm_isp_bw_req_info *) t_file->private_data;
@@ -210,7 +207,7 @@ static int ub_info_open(struct inode *inode, struct file *file)
return 0;
}
-static ssize_t ub_info_read(struct file *t_file, char *t_char,
+static ssize_t ub_info_read(struct file *t_file, char __user *t_char,
size_t t_size_t, loff_t *t_loff_t)
{
int i;
@@ -241,7 +238,7 @@ static ssize_t ub_info_read(struct file *t_file, char *t_char,
}
static ssize_t ub_info_write(struct file *t_file,
- const char *t_char, size_t t_size_t, loff_t *t_loff_t)
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
{
struct vfe_device *vfe_dev =
(struct vfe_device *) t_file->private_data;
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.c b/drivers/media/platform/msm/ais/isp/msm_isp47.c
index 8991433b2c67..d63282f80aca 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp47.c
@@ -2708,7 +2708,6 @@ struct msm_vfe_hardware_info vfe47_hw_info = {
.process_camif_irq = msm_vfe47_process_input_irq,
.process_reset_irq = msm_vfe47_process_reset_irq,
.process_halt_irq = msm_vfe47_process_halt_irq,
- .process_reset_irq = msm_vfe47_process_reset_irq,
.process_reg_update = msm_vfe47_process_reg_update,
.process_axi_irq = msm_isp_process_axi_irq,
.process_stats_irq = msm_isp_process_stats_irq,
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.h b/drivers/media/platform/msm/ais/isp/msm_isp47.h
index b29fca61ce7c..9af0acd3656a 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp47.h
+++ b/drivers/media/platform/msm/ais/isp/msm_isp47.h
@@ -30,6 +30,8 @@ enum msm_vfe47_stats_comp_idx {
extern struct msm_vfe_hardware_info vfe47_hw_info;
+uint32_t msm_vfe47_ub_reg_offset(struct vfe_device *vfe_dev, int wm_idx);
+uint32_t msm_vfe47_get_ub_size(struct vfe_device *vfe_dev);
void msm_vfe47_read_irq_status(struct vfe_device *vfe_dev,
uint32_t *irq_status0, uint32_t *irq_status1);
void msm_vfe47_read_irq_status_and_clear(struct vfe_device *vfe_dev,
@@ -70,6 +72,8 @@ int32_t msm_vfe47_cfg_io_format(struct vfe_device *vfe_dev,
enum msm_vfe_axi_stream_src stream_src, uint32_t io_format);
int msm_vfe47_start_fetch_engine(struct vfe_device *vfe_dev,
void *arg);
+int msm_vfe47_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev,
+ void *arg);
void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev,
struct msm_vfe_pix_cfg *pix_cfg);
void msm_vfe47_cfg_testgen(struct vfe_device *vfe_dev,
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
index 373a963f75aa..a85ee30769c4 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
@@ -3889,6 +3889,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
case UPDATE_STREAM_REQUEST_FRAMES_VER2: {
struct msm_vfe_axi_stream_cfg_update_info_req_frm *req_frm =
&update_cmd->req_frm_ver2;
+ if (HANDLE_TO_IDX(req_frm->stream_handle) >= VFE_AXI_SRC_MAX) {
+ pr_err("%s: Invalid stream handle\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+
stream_info = &axi_data->stream_info[HANDLE_TO_IDX(
req_frm->stream_handle)];
rc = msm_isp_request_frame(vfe_dev, stream_info,
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
index 0396fc4680f1..5ed89161b7f3 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
@@ -27,6 +27,9 @@ int msm_isp_axi_create_stream(struct vfe_device *vfe_dev,
void msm_isp_axi_destroy_stream(
struct msm_vfe_axi_shared_data *axi_data, int stream_idx);
+int msm_isp_axi_get_num_planes(uint32_t output_format,
+ struct msm_vfe_axi_stream *stream_info);
+
int msm_isp_validate_axi_request(
struct msm_vfe_axi_shared_data *axi_data,
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
@@ -36,21 +39,34 @@ void msm_isp_axi_reserve_wm(
struct msm_vfe_axi_shared_data *axi_data,
struct msm_vfe_axi_stream *stream_info);
+void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info);
+
void msm_isp_axi_reserve_comp_mask(
struct msm_vfe_axi_shared_data *axi_data,
struct msm_vfe_axi_stream *stream_info);
+void msm_isp_axi_free_comp_mask(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info);
+
int msm_isp_axi_check_stream_state(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd);
+void msm_isp_check_for_output_error(struct vfe_device *vfe_dev,
+ struct msm_isp_timestamp *ts, struct msm_isp_sof_info *sof_info);
+
int msm_isp_calculate_framedrop(
struct msm_vfe_axi_shared_data *axi_data,
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+void msm_isp_calculate_bandwidth(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info);
void msm_isp_reset_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info);
int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_start_avtimer(void);
void msm_isp_get_avtimer_ts(struct msm_isp_timestamp *time_stamp);
int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev,
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c
index 3b877b4cb994..6e89544161ee 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c
@@ -863,6 +863,12 @@ int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
if (vfe_dev->stats_data.num_active_stream == 0)
vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev);
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
+
if (stream_cfg_cmd->enable) {
msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd);
@@ -891,7 +897,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/ais/isp/msm_isp_stats_util.h b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.h
index 707901bc6271..ae438a675542 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.h
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.h
@@ -19,6 +19,8 @@
void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
uint32_t pingpong_status, struct msm_isp_timestamp *ts);
+int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd);
void msm_isp_stats_stream_update(struct vfe_device *vfe_dev);
int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg);
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_util.c
index 0353ab27cf19..9e5317eb2920 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_util.c
@@ -512,7 +512,7 @@ static int msm_isp_cfg_rdi(struct vfe_device *vfe_dev,
return rc;
}
-int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
+static int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0;
struct msm_vfe_input_cfg *input_cfg = arg;
@@ -542,7 +542,7 @@ int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
return rc;
}
-int msm_isp_camif_cfg(struct vfe_device *vfe_dev, void *arg)
+static int msm_isp_camif_cfg(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0;
struct msm_vfe_camif_cfg *camif_cfg = arg;
@@ -579,7 +579,7 @@ int msm_isp_camif_cfg(struct vfe_device *vfe_dev, void *arg)
}
-int msm_isp_operation_cfg(struct vfe_device *vfe_dev, void *arg)
+static int msm_isp_operation_cfg(struct vfe_device *vfe_dev, void *arg)
{
struct msm_vfe_operation_cfg *op_cfg = arg;
@@ -1233,14 +1233,16 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev,
case VFE_WRITE: {
msm_camera_io_memcpy(vfe_dev->vfe_base +
reg_cfg_cmd->u.rw_info.reg_offset,
- cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4,
+ (void __iomem *)
+ (cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4),
reg_cfg_cmd->u.rw_info.len);
break;
}
case VFE_WRITE_MB: {
msm_camera_io_memcpy_mb(vfe_dev->vfe_base +
reg_cfg_cmd->u.rw_info.reg_offset,
- cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4,
+ (void __iomem *)
+ (cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4),
reg_cfg_cmd->u.rw_info.len);
break;
}
@@ -2295,12 +2297,12 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
}
#ifdef CONFIG_MSM_AVTIMER
-void msm_isp_end_avtimer(void)
+static void msm_isp_end_avtimer(void)
{
avcs_core_disable_power_collapse(0);
}
#else
-void msm_isp_end_avtimer(void)
+static void msm_isp_end_avtimer(void)
{
pr_err("AV Timer is not supported\n");
}
@@ -2408,7 +2410,7 @@ void msm_isp_save_framedrop_values(struct vfe_device *vfe_dev,
}
}
-void msm_isp_dump_irq_debug(void)
+static void msm_isp_dump_irq_debug(void)
{
uint32_t index, count, i;
diff --git a/drivers/media/platform/msm/ais/msm.c b/drivers/media/platform/msm/ais/msm.c
index e8859b7db5cb..3c2e28eaa4f9 100644
--- a/drivers/media/platform/msm/ais/msm.c
+++ b/drivers/media/platform/msm/ais/msm.c
@@ -48,10 +48,10 @@ bool is_daemon_status = true;
/* config node envent queue */
static struct v4l2_fh *msm_eventq;
-spinlock_t msm_eventq_lock;
+static spinlock_t msm_eventq_lock;
static struct pid *msm_pid;
-spinlock_t msm_pid_lock;
+static spinlock_t msm_pid_lock;
/*
* It takes 20 bytes + NULL character to write the
@@ -62,7 +62,7 @@ spinlock_t msm_pid_lock;
#define msm_dequeue(queue, type, member) ({ \
unsigned long flags; \
struct msm_queue_head *__q = (queue); \
- type *node = 0; \
+ type *node = NULL; \
spin_lock_irqsave(&__q->lock, flags); \
if (!list_empty(&__q->list)) { \
__q->len--; \
@@ -78,7 +78,7 @@ spinlock_t msm_pid_lock;
#define msm_delete_sd_entry(queue, type, member, q_node) ({ \
unsigned long flags; \
struct msm_queue_head *__q = (queue); \
- type *node = 0; \
+ type *node = NULL; \
spin_lock_irqsave(&__q->lock, flags); \
if (!list_empty(&__q->list)) { \
list_for_each_entry(node, &__q->list, member) \
@@ -95,7 +95,7 @@ spinlock_t msm_pid_lock;
#define msm_delete_entry(queue, type, member, q_node) ({ \
unsigned long flags; \
struct msm_queue_head *__q = (queue); \
- type *node = 0; \
+ type *node = NULL; \
spin_lock_irqsave(&__q->lock, flags); \
if (!list_empty(&__q->list)) { \
list_for_each_entry(node, &__q->list, member) \
@@ -131,7 +131,7 @@ typedef int (*msm_queue_func)(void *d1, void *d2);
#define msm_queue_traverse_action(queue, type, member, func, data) do {\
unsigned long flags; \
struct msm_queue_head *__q = (queue); \
- type *node = 0; \
+ type *node = NULL; \
msm_queue_func __f = (func); \
spin_lock_irqsave(&__q->lock, flags); \
if (!list_empty(&__q->list)) { \
@@ -147,7 +147,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); \
@@ -283,22 +283,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);
@@ -446,6 +471,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);
return 0;
}
EXPORT_SYMBOL(msm_create_session);
@@ -1040,17 +1066,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);
@@ -1108,6 +1142,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,
@@ -1119,7 +1181,7 @@ long msm_copy_camera_private_ioctl_args(unsigned long arg,
return -EIO;
if (copy_from_user(&up_ioctl,
- (struct msm_camera_private_ioctl_arg *)arg,
+ (void __user *)arg,
sizeof(struct msm_camera_private_ioctl_arg)))
return -EFAULT;
diff --git a/drivers/media/platform/msm/ais/msm.h b/drivers/media/platform/msm/ais/msm.h
index d8b2d5871fc2..5d456310c301 100644
--- a/drivers/media/platform/msm/ais/msm.h
+++ b/drivers/media/platform/msm/ais/msm.h
@@ -114,6 +114,7 @@ struct msm_session {
struct mutex lock;
struct mutex lock_q;
struct mutex close_lock;
+ rwlock_t stream_rwlock;
};
static inline bool msm_is_daemon_present(void)
@@ -131,11 +132,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/ais/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/ais/msm_buf_mgr/msm_generic_buf_mgr.c
index 073b91a6d2d9..66751b1f0657 100644
--- a/drivers/media/platform/msm/ais/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/ais/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -51,7 +51,7 @@ static int32_t msm_buf_mngr_hdl_cont_get_buf(struct msm_buf_mngr_device *dev,
}
static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *dev,
- void __user *argp)
+ void *argp)
{
unsigned long flags;
int32_t rc = 0;
@@ -465,7 +465,7 @@ static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd,
return rc;
}
-int msm_cam_buf_mgr_ops(unsigned int cmd, void *argp)
+static int msm_cam_buf_mgr_ops(unsigned int cmd, void *argp)
{
int rc = 0;
@@ -531,7 +531,7 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd,
{
int32_t rc = 0;
struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
- void __user *argp = (void __user *)arg;
+ void *argp = arg;
if (!buf_mngr_dev) {
pr_err("%s buf manager device NULL\n", __func__);
@@ -557,13 +557,13 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd,
MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl.ioctl_ptr,
sizeof(tmp));
- if (copy_from_user(&buf_info, tmp,
+ if (copy_from_user(&buf_info, (void __user *)tmp,
sizeof(struct msm_buf_mngr_info))) {
return -EFAULT;
}
k_ioctl.ioctl_ptr = (uintptr_t)&buf_info;
- argp = &k_ioctl;
+ argp = (void *)&k_ioctl;
rc = msm_cam_buf_mgr_ops(cmd, argp);
}
break;
diff --git a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c
index 280bf4ebb596..36aa3f62fbec 100644
--- a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c
@@ -41,20 +41,28 @@ static int msm_vb2_queue_setup(struct vb2_queue *q,
return 0;
}
-int msm_vb2_buf_init(struct vb2_buffer *vb)
+static 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,18 +136,27 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
}
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
}
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;
}
@@ -142,8 +176,28 @@ 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,
.buf_init = msm_vb2_buf_init,
@@ -199,13 +253,22 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
{
struct msm_stream *stream;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ struct msm_session *session;
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;
}
@@ -236,12 +300,22 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
{
struct msm_stream *stream;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ struct msm_session *session;
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,15 +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) {
@@ -306,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;
}
@@ -317,11 +402,21 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ struct msm_session *session;
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) {
@@ -353,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;
}
@@ -361,14 +457,23 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
{
struct msm_stream *stream;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ struct msm_session *session;
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) {
@@ -394,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);
@@ -404,10 +510,20 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ struct msm_session *session;
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ 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);
@@ -416,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/ais/msm_vb2/msm_vb2.h b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.h
index 3dbb21332857..0f57112e82f2 100644
--- a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.h
+++ b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.h
@@ -68,5 +68,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/ais/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c
index 8df56fe526fe..1adb380f335f 100644
--- a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c
+++ b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c
@@ -522,7 +522,7 @@ static int32_t msm_actuator_piezo_move_focus(
CDBG("Enter\n");
if (copy_from_user(&ringing_params_kernel,
- &(move_params->ringing_params[0]),
+ (void __user *)&(move_params->ringing_params[0]),
sizeof(struct damping_params_t))) {
pr_err("copy_from_user failed\n");
return -EFAULT;
@@ -612,7 +612,7 @@ static int32_t msm_actuator_move_focus(
return -EFAULT;
}
if (copy_from_user(ringing_params_kernel,
- &(move_params->ringing_params[0]),
+ (void __user *)&(move_params->ringing_params[0]),
(sizeof(struct damping_params_t))*(a_ctrl->region_size))) {
pr_err("copy_from_user failed\n");
/* Free the allocated memory for damping parameters */
@@ -732,7 +732,7 @@ static int32_t msm_actuator_bivcm_move_focus(
return -EFAULT;
}
if (copy_from_user(ringing_params_kernel,
- &(move_params->ringing_params[0]),
+ (void __user *)&(move_params->ringing_params[0]),
(sizeof(struct damping_params_t))*(a_ctrl->region_size))) {
pr_err("copy_from_user failed\n");
/* Free the allocated memory for damping parameters */
@@ -1289,7 +1289,7 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl,
a_ctrl->total_steps = set_info->af_tuning_params.total_steps;
if (copy_from_user(&a_ctrl->region_params,
- (void *)set_info->af_tuning_params.region_params,
+ (void __user *)set_info->af_tuning_params.region_params,
a_ctrl->region_size * sizeof(struct region_params_t)))
return -EFAULT;
@@ -1332,7 +1332,7 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl,
}
if (copy_from_user(&a_ctrl->reg_tbl,
- (void *)set_info->actuator_params.reg_tbl_params,
+ (void __user *)set_info->actuator_params.reg_tbl_params,
a_ctrl->reg_tbl_size *
sizeof(struct msm_actuator_reg_params_t))) {
kfree(a_ctrl->i2c_reg_tbl);
@@ -1354,7 +1354,8 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl,
return -EFAULT;
}
if (copy_from_user(init_settings,
- (void *)set_info->actuator_params.init_settings,
+ (void __user *)
+ set_info->actuator_params.init_settings,
set_info->actuator_params.init_setting_size *
sizeof(struct reg_settings_t))) {
kfree(init_settings);
@@ -1411,7 +1412,7 @@ static int msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl)
}
static int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl,
- void __user *argp)
+ void *argp)
{
struct msm_actuator_cfg_data *cdata =
(struct msm_actuator_cfg_data *)argp;
@@ -1571,7 +1572,7 @@ static long msm_actuator_subdev_ioctl(struct v4l2_subdev *sd,
{
int rc;
struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd);
- void __user *argp = (void __user *)arg;
+ void *argp = arg;
CDBG("Enter\n");
CDBG("%s:%d a_ctrl %pK argp %pK\n", __func__, __LINE__, a_ctrl, argp);
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h
index f88c0ef82499..f55e6c344ef1 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h
@@ -15,9 +15,9 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v2_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+static uint8_t csid_lane_assign_v2_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
-struct csid_reg_parms_t csid_v2_0 = {
+static struct csid_reg_parms_t csid_v2_0 = {
/* MIPI CSID registers */
0x0,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h
index e2bb6cd499ff..9ba3555ff01f 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h
@@ -15,9 +15,9 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v2_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+static uint8_t csid_lane_assign_v2_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
-struct csid_reg_parms_t csid_v2_2 = {
+static struct csid_reg_parms_t csid_v2_2 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h
index 440f869692f7..c75c4167453c 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h
@@ -15,9 +15,9 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+static uint8_t csid_lane_assign_v3_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
-struct csid_reg_parms_t csid_v3_0 = {
+static struct csid_reg_parms_t csid_v3_0 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h
index dde47046b679..dc71f39a38f1 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h
@@ -15,9 +15,9 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+static uint8_t csid_lane_assign_v3_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
-struct csid_reg_parms_t csid_v3_1 = {
+static struct csid_reg_parms_t csid_v3_1 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h
index 5241a90fbc86..00085dbf94a0 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h
@@ -15,9 +15,9 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+static uint8_t csid_lane_assign_v3_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
-struct csid_reg_parms_t csid_v3_2 = {
+static struct csid_reg_parms_t csid_v3_2 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h
index 0e8ff6c0986d..1d465b66b33f 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h
@@ -14,9 +14,9 @@
#define MSM_CSID_3_4_1_HWREG_H
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_4_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+static uint8_t csid_lane_assign_v3_4_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
-struct csid_reg_parms_t csid_v3_4_1 = {
+static struct csid_reg_parms_t csid_v3_4_1 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h
index 651526cb3db8..d78e68e090e7 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h
@@ -15,8 +15,8 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_4_2[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
-struct csid_reg_parms_t csid_v3_4_2 = {
+static uint8_t csid_lane_assign_v3_4_2[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
+static struct csid_reg_parms_t csid_v3_4_2 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h
index fff29fc9d4c4..bbf4b287ffe4 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h
@@ -15,8 +15,8 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_4_3[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
-struct csid_reg_parms_t csid_v3_4_3 = {
+static uint8_t csid_lane_assign_v3_4_3[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
+static struct csid_reg_parms_t csid_v3_4_3 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h
index f7d7d3548c4b..534ef3f5533c 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h
@@ -15,9 +15,9 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_5_1[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
+static uint8_t csid_lane_assign_v3_5_1[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
-struct csid_reg_parms_t csid_v3_5_1 = {
+static struct csid_reg_parms_t csid_v3_5_1 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h
index b423b6e510a0..392d902d3e0c 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h
@@ -15,9 +15,9 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_5[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
+static uint8_t csid_lane_assign_v3_5[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
-struct csid_reg_parms_t csid_v3_5 = {
+static struct csid_reg_parms_t csid_v3_5 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h
index b95a774ca737..6722974f889b 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h
@@ -15,8 +15,8 @@
#include <sensor/csid/msm_csid.h>
-uint8_t csid_lane_assign_v3_6_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
-struct csid_reg_parms_t csid_v3_6_0 = {
+static uint8_t csid_lane_assign_v3_6_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+static struct csid_reg_parms_t csid_v3_6_0 = {
/* MIPI CSID registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c b/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c
index 331ba939adfa..2b3eefa65606 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c
@@ -708,7 +708,7 @@ static int msm_csid_release(struct csid_device *csid_dev)
return 0;
}
-static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg)
+static int32_t msm_csid_cmd(struct csid_device *csid_dev, void *arg)
{
int rc = 0;
struct csid_cfg_data *cdata = (struct csid_cfg_data *)arg;
@@ -728,7 +728,7 @@ static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg)
case CSID_TESTMODE_CFG: {
csid_dev->is_testmode = 1;
if (copy_from_user(&csid_dev->testmode_params,
- (void *)cdata->cfg.csid_testmode_params,
+ (void __user *)cdata->cfg.csid_testmode_params,
sizeof(struct msm_camera_csid_testmode_parms))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -741,7 +741,7 @@ static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg)
int i = 0;
if (copy_from_user(&csid_params,
- (void *)cdata->cfg.csid_params,
+ (void __user *)cdata->cfg.csid_params,
sizeof(struct msm_camera_csid_params))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -790,7 +790,7 @@ static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg)
int i = 0;
if (copy_from_user(&csid_params,
- (void *)cdata->cfg.csid_params,
+ (void __user *)cdata->cfg.csid_params,
sizeof(struct msm_camera_csid_params))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -892,7 +892,7 @@ static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd,
#ifdef CONFIG_COMPAT
-static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg)
+static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void *arg)
{
int rc = 0;
struct csid_cfg_data32 *arg32 = (struct csid_cfg_data32 *) (arg);
@@ -913,7 +913,8 @@ static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg)
case CSID_TESTMODE_CFG: {
csid_dev->is_testmode = 1;
if (copy_from_user(&csid_dev->testmode_params,
- (void *)compat_ptr(arg32->cfg.csid_testmode_params),
+ (void __user *)
+ compat_ptr(arg32->cfg.csid_testmode_params),
sizeof(struct msm_camera_csid_testmode_parms))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -926,7 +927,7 @@ static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg)
struct msm_camera_csid_params32 csid_params32;
if (copy_from_user(&csid_params32,
- (void *)compat_ptr(arg32->cfg.csid_params),
+ (void __user *)compat_ptr(arg32->cfg.csid_params),
sizeof(struct msm_camera_csid_params32))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -975,7 +976,7 @@ static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg)
struct msm_camera_csid_params32 csid_params32;
if (copy_from_user(&csid_params32,
- (void *)compat_ptr(arg32->cfg.csid_params),
+ (void __user *)compat_ptr(arg32->cfg.csid_params),
sizeof(struct msm_camera_csid_params32))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h
index 618926fa8341..3b377de66a2c 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h
@@ -15,7 +15,7 @@
#include <sensor/csiphy/msm_csiphy.h>
-struct csiphy_reg_parms_t csiphy_v2_0 = {
+static struct csiphy_reg_parms_t csiphy_v2_0 = {
/* MIPI CSI PHY registers */
0x17C,
0x0,
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h
index 867aec2e0103..71b07299c342 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h
@@ -15,7 +15,7 @@
#include <sensor/csiphy/msm_csiphy.h>
-struct csiphy_reg_parms_t csiphy_v2_2 = {
+static struct csiphy_reg_parms_t csiphy_v2_2 = {
/* MIPI CSI PHY registers */
0x17C,
0x0,
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h
index 69efdcc71499..8846fde0f6ed 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h
@@ -15,7 +15,7 @@
#include <sensor/csiphy/msm_csiphy.h>
-struct csiphy_reg_parms_t csiphy_v3_0 = {
+static struct csiphy_reg_parms_t csiphy_v3_0 = {
/* MIPI CSI PHY registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h
index 7fc74a366a6c..044e16ef3848 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h
@@ -15,7 +15,7 @@
#include <sensor/csiphy/msm_csiphy.h>
-struct csiphy_reg_parms_t csiphy_v3_1 = {
+static struct csiphy_reg_parms_t csiphy_v3_1 = {
/* MIPI CSI PHY registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h
index cdf62d46ee7d..c01f0540dfd2 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h
@@ -15,7 +15,7 @@
#include <sensor/csiphy/msm_csiphy.h>
-struct csiphy_reg_parms_t csiphy_v3_2 = {
+static struct csiphy_reg_parms_t csiphy_v3_2 = {
/* MIPI CSI PHY registers */
0x0,
0x4,
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h
index 5af1ded189a6..78ac19993fee 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h
@@ -18,14 +18,14 @@
#include <sensor/csiphy/msm_csiphy.h>
-struct csiphy_reg_parms_t csiphy_v3_4_2_1 = {
+static struct csiphy_reg_parms_t csiphy_v3_4_2_1 = {
.mipi_csiphy_interrupt_status0_addr = 0x8B0,
.mipi_csiphy_interrupt_clear0_addr = 0x858,
.mipi_csiphy_glbl_irq_cmd_addr = 0x828,
.combo_clk_mask = 0x10,
};
-struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_1_3ph = {
+static struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_1_3ph = {
/* MIPI CSI PHY registers */
{0x814, 0x0},
{0x818, 0x1},
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h
index d85dd1ec3a48..e6072e747a63 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h
@@ -18,14 +18,14 @@
#include <sensor/csiphy/msm_csiphy.h>
-struct csiphy_reg_parms_t csiphy_v3_4_2 = {
+static struct csiphy_reg_parms_t csiphy_v3_4_2 = {
.mipi_csiphy_interrupt_status0_addr = 0x8B0,
.mipi_csiphy_interrupt_clear0_addr = 0x858,
.mipi_csiphy_glbl_irq_cmd_addr = 0x828,
.combo_clk_mask = 0x10,
};
-struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_3ph = {
+static struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_3ph = {
/* MIPI CSI PHY registers */
{0x814, 0x0},
{0x818, 0x1},
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h
index 99b725a75c8f..bc70697cce5c 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h
@@ -18,14 +18,14 @@
#include <sensor/csiphy/msm_csiphy.h>
-struct csiphy_reg_parms_t csiphy_v3_5 = {
+static struct csiphy_reg_parms_t csiphy_v3_5 = {
.mipi_csiphy_interrupt_status0_addr = 0x8B0,
.mipi_csiphy_interrupt_clear0_addr = 0x858,
.mipi_csiphy_glbl_irq_cmd_addr = 0x828,
.combo_clk_mask = 0x10,
};
-struct csiphy_reg_3ph_parms_t csiphy_v3_5_3ph = {
+static struct csiphy_reg_3ph_parms_t csiphy_v3_5_3ph = {
/* MIPI CSI PHY registers */
{0x814, 0x0},
{0x818, 0x1},
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c
index d146cc3d28a5..c3b087f61888 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c
@@ -164,7 +164,7 @@ static int msm_csiphy_3phase_lane_config(
mipi_csiphy_3ph_lnn_ctrl1.data,
csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
mipi_csiphy_3ph_lnn_ctrl1.addr + 0x200*i);
- msm_camera_io_w(((csiphy_params->settle_cnt >> 8) & 0xff),
+ msm_camera_io_w(0,
csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
mipi_csiphy_3ph_lnn_ctrl2.addr + 0x200*i);
msm_camera_io_w((csiphy_params->settle_cnt & 0xff),
@@ -648,7 +648,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev,
return rc;
}
-void msm_csiphy_disable_irq(
+static void msm_csiphy_disable_irq(
struct csiphy_device *csiphy_dev)
{
void __iomem *csiphybase;
@@ -1207,7 +1207,7 @@ static int32_t msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg)
break;
case CSIPHY_CFG:
if (copy_from_user(&csiphy_params,
- (void *)cdata->cfg.csiphy_params,
+ (void __user *)cdata->cfg.csiphy_params,
sizeof(struct msm_camera_csiphy_params))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
diff --git a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c
index b97156cbd486..6af589e5c230 100644
--- a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c
+++ b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c
@@ -54,7 +54,7 @@ static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = {
.i2c_poll = msm_camera_cci_i2c_poll,
};
-void msm_torch_brightness_set(struct led_classdev *led_cdev,
+static void msm_torch_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (!torch_trigger) {
@@ -202,7 +202,7 @@ static int32_t msm_flash_i2c_init(
}
if (copy_from_user(power_setting_array32,
- (void *)flash_init_info->power_setting_array,
+ (void __user *)flash_init_info->power_setting_array,
sizeof(struct msm_sensor_power_setting_array32))) {
pr_err("%s copy_from_user failed %d\n",
__func__, __LINE__);
@@ -248,7 +248,7 @@ static int32_t msm_flash_i2c_init(
} else
#endif
if (copy_from_user(&flash_ctrl->power_setting_array,
- (void *)flash_init_info->power_setting_array,
+ (void __user *)flash_init_info->power_setting_array,
sizeof(struct msm_sensor_power_setting_array))) {
pr_err("%s copy_from_user failed %d\n", __func__, __LINE__);
return -EFAULT;
@@ -298,7 +298,8 @@ static int32_t msm_flash_i2c_init(
goto msm_flash_i2c_init_fail;
}
- if (copy_from_user(settings, (void *)flash_init_info->settings,
+ if (copy_from_user(settings,
+ (void __user *)flash_init_info->settings,
sizeof(struct msm_camera_i2c_reg_setting_array))) {
kfree(settings);
pr_err("%s copy_from_user failed %d\n",
@@ -414,7 +415,7 @@ static int32_t msm_flash_i2c_write_setting_array(
if (!settings)
return -ENOMEM;
- if (copy_from_user(settings, (void *)flash_data->cfg.settings,
+ if (copy_from_user(settings, (void __user *)flash_data->cfg.settings,
sizeof(struct msm_camera_i2c_reg_setting_array))) {
kfree(settings);
pr_err("%s copy_from_user failed %d\n", __func__, __LINE__);
@@ -626,7 +627,7 @@ static int32_t msm_flash_release(
}
static int32_t msm_flash_config(struct msm_flash_ctrl_t *flash_ctrl,
- void __user *argp)
+ void *argp)
{
int32_t rc = 0;
struct msm_flash_cfg_data_t *flash_data =
@@ -701,7 +702,7 @@ static long msm_flash_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
struct msm_flash_ctrl_t *fctrl = NULL;
- void __user *argp = (void __user *)arg;
+ void *argp = arg;
CDBG("Enter\n");
@@ -1038,7 +1039,8 @@ static long msm_flash_subdev_do_ioctl(
case CFG_FLASH_INIT:
flash_data.cfg.flash_init_info = &flash_init_info;
if (copy_from_user(&flash_init_info32,
- (void *)compat_ptr(u32->cfg.flash_init_info),
+ (void __user *)
+ compat_ptr(u32->cfg.flash_init_info),
sizeof(struct msm_flash_init_info_t32))) {
pr_err("%s copy_from_user failed %d\n",
__func__, __LINE__);
diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c
index 955be342e8cf..8f2fd0f9e24d 100644
--- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c
+++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c
@@ -23,7 +23,7 @@ int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
enum msm_camera_i2c_data_type data_type)
{
int32_t rc = -EFAULT;
- unsigned char buf[client->addr_type+data_type];
+ unsigned char *buf = NULL;
struct msm_camera_cci_ctrl cci_ctrl;
if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
@@ -33,6 +33,11 @@ int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
&& data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
+ buf = kzalloc((uint32_t)client->addr_type + (uint32_t)data_type,
+ GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
cci_ctrl.cmd = MSM_CCI_I2C_READ;
cci_ctrl.cci_info = client->cci_client;
cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr;
@@ -42,6 +47,8 @@ int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
rc = v4l2_subdev_call(client->cci_client->cci_subdev,
core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
if (rc < 0) {
+ kfree(buf);
+ buf = NULL;
pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
return rc;
}
@@ -51,6 +58,8 @@ int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
else
*data = buf[0] << 8 | buf[1];
+ kfree(buf);
+ buf = NULL;
S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data);
return rc;
}
diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c
index 071600ed5221..66300e3f7359 100644
--- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c
+++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c
@@ -685,7 +685,7 @@ ERROR2:
kfree(array);
ERROR1:
kfree(ps);
- power_setting_size = 0;
+ power_setting_size = NULL;
return rc;
}
diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h b/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h
index a29ef21274c2..fdeeb4aebf00 100644
--- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h
+++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h
@@ -62,6 +62,9 @@ int msm_camera_fill_vreg_params(struct camera_vreg_t *cam_vreg,
int msm_camera_pinctrl_init
(struct msm_pinctrl_info *sensor_pctrl, struct device *dev);
+int msm_cam_sensor_handle_reg_gpio(int seq_val,
+ struct msm_camera_gpio_conf *gconf, int val);
+
int32_t msm_sensor_driver_get_gpio_data(
struct msm_camera_gpio_conf **gpio_conf,
struct device_node *of_node);
diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c
index 9098b23dbc67..449951f5ffad 100644
--- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c
+++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c
@@ -88,7 +88,8 @@ int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client,
return rc;
}
- buf = kzalloc(client->addr_type+data_type, GFP_KERNEL);
+ buf = kzalloc((uint32_t)client->addr_type + (uint32_t)data_type,
+ GFP_KERNEL);
if (!buf) {
S_I2C_DBG("%s:%d no memory\n", __func__, __LINE__);
return -ENOMEM;
@@ -179,7 +180,7 @@ int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
enum msm_camera_i2c_data_type data_type)
{
int32_t rc = -EFAULT;
- unsigned char buf[client->addr_type+data_type];
+ unsigned char *buf = NULL;
uint8_t len = 0;
if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
@@ -188,6 +189,11 @@ int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
&& data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
+ buf = kzalloc((uint32_t)client->addr_type + (uint32_t)data_type,
+ GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
S_I2C_DBG("%s reg addr = 0x%x data type: %d\n",
__func__, addr, data_type);
if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
@@ -219,6 +225,9 @@ int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
rc = msm_camera_qup_i2c_txdata(client, buf, len);
if (rc < 0)
S_I2C_DBG("%s fail\n", __func__);
+
+ kfree(buf);
+ buf = NULL;
return rc;
}
@@ -226,7 +235,7 @@ int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
uint32_t addr, uint8_t *data, uint32_t num_byte)
{
int32_t rc = -EFAULT;
- unsigned char buf[client->addr_type+num_byte];
+ unsigned char *buf = NULL;
uint8_t len = 0, i = 0;
if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
@@ -234,6 +243,10 @@ int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
|| num_byte == 0)
return rc;
+ buf = kzalloc(client->addr_type+num_byte, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n",
__func__, addr, num_byte);
if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
@@ -263,6 +276,9 @@ int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
rc = msm_camera_qup_i2c_txdata(client, buf, len+num_byte);
if (rc < 0)
S_I2C_DBG("%s fail\n", __func__);
+
+ kfree(buf);
+ buf = NULL;
return rc;
}
diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c b/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c
index cd277f0ca0da..d0e27bcc4aac 100644
--- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c
+++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c
@@ -513,7 +513,7 @@ int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client,
&client->spi_client->cmd_tbl.page_program;
uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
uint16_t len = 0;
- char buf[data_type];
+ char *buf = NULL;
char *tx;
int rc = -EINVAL;
@@ -524,10 +524,13 @@ int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client,
&& data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
S_I2C_DBG("Data: 0x%x\n", data);
+ buf = kzalloc(data_type, GFP_KERNEL);
+ if (!buf)
+ goto NOMEM;
len = header_len + (uint8_t)data_type;
tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!tx)
- goto NOMEM;
+ goto FREEBUF;
if (data_type == MSM_CAMERA_I2C_BYTE_DATA) {
buf[0] = data;
SPIDBG("Byte %d: 0x%x\n", len, buf[0]);
@@ -540,6 +543,8 @@ int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client,
if (rc < 0)
goto ERROR;
goto OUT;
+FREEBUF:
+ kfree(buf);
NOMEM:
pr_err("%s: memory allocation failed\n", __func__);
return -ENOMEM;
@@ -547,6 +552,7 @@ ERROR:
pr_err("%s: error write\n", __func__);
OUT:
kfree(tx);
+ kfree(buf);
return rc;
}
int32_t msm_camera_spi_write_table(struct msm_camera_i2c_client *client,
@@ -585,7 +591,7 @@ int32_t msm_camera_spi_write_table(struct msm_camera_i2c_client *client,
client->addr_type = client_addr_type;
return rc;
}
-uint32_t msm_get_burst_size(struct msm_camera_i2c_reg_array *reg_setting,
+static uint32_t msm_get_burst_size(struct msm_camera_i2c_reg_array *reg_setting,
uint32_t reg_size, uint32_t index, uint16_t burst_addr)
{
uint32_t i;
@@ -601,7 +607,7 @@ uint32_t msm_get_burst_size(struct msm_camera_i2c_reg_array *reg_setting,
}
#ifdef SPI_DYNAMIC_ALLOC
-int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client,
+static int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
struct msm_camera_burst_info *info,
enum msm_camera_i2c_data_type data_type)
@@ -677,7 +683,7 @@ fail:
return rc;
}
#else /* SPI_DYNAMIC_ALLOC */
-int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client,
+static int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
struct msm_camera_burst_info *info,
enum msm_camera_i2c_data_type data_type)
diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h b/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h
index 28aa184ce630..9f87db1dbbfa 100644
--- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h
+++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h
@@ -83,6 +83,14 @@ uint16_t msm_camera_spi_get_hlen(struct msm_camera_spi_inst *inst)
return sizeof(inst->opcode) + inst->addr_len + inst->dummy_len;
}
+int32_t msm_camera_spi_tx_helper(struct msm_camera_i2c_client *client,
+ struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
+ uint32_t num_byte, char *tx, char *rx);
+
+int32_t msm_camera_spi_tx_read(struct msm_camera_i2c_client *client,
+ struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
+ uint32_t num_byte, char *tx, char *rx);
+
int32_t msm_camera_spi_read(struct msm_camera_i2c_client *client,
uint32_t addr, uint16_t *data,
enum msm_camera_i2c_data_type data_type);
diff --git a/drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c b/drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c
index bfb960ea862a..68ab4003b666 100644
--- a/drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c
+++ b/drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c
@@ -282,7 +282,7 @@ static int32_t msm_ir_cut_handle_init(
}
static int32_t msm_ir_cut_config(struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
- void __user *argp)
+ void *argp)
{
int32_t rc = -EINVAL;
struct msm_ir_cut_cfg_data_t *ir_cut_data =
@@ -327,7 +327,7 @@ static long msm_ir_cut_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
struct msm_ir_cut_ctrl_t *fctrl = NULL;
- void __user *argp = (void __user *)arg;
+ void *argp = arg;
CDBG("Enter\n");
diff --git a/drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c b/drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c
index 803bce440ee1..9e200071f9eb 100644
--- a/drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c
+++ b/drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c
@@ -196,7 +196,7 @@ static int32_t msm_ir_led_handle_init(
}
static int32_t msm_ir_led_config(struct msm_ir_led_ctrl_t *ir_led_ctrl,
- void __user *argp)
+ void *argp)
{
int32_t rc = -EINVAL;
struct msm_ir_led_cfg_data_t *ir_led_data =
@@ -241,7 +241,7 @@ static long msm_ir_led_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
struct msm_ir_led_ctrl_t *fctrl = NULL;
- void __user *argp = (void __user *)arg;
+ void *argp = arg;
struct msm_ir_led_cfg_data_t ir_led_data = {0};
if (!sd) {
diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.c b/drivers/media/platform/msm/ais/sensor/msm_sensor.c
index c671ea71d2a7..a276b03e5294 100644
--- a/drivers/media/platform/msm/ais/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.c
@@ -343,7 +343,7 @@ static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
{
int rc = 0;
struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
- void __user *argp = (void __user *)arg;
+ void *argp = arg;
if (!s_ctrl) {
pr_err("%s s_ctrl NULL\n", __func__);
@@ -421,7 +421,7 @@ long msm_sensor_subdev_fops_ioctl(struct file *file,
}
static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
- void __user *argp)
+ void *argp)
{
struct sensorb_cfg_data32 *cdata = (struct sensorb_cfg_data32 *)argp;
int32_t rc = 0;
@@ -498,7 +498,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
}
if (copy_from_user(&conf_array32,
- (void *)compat_ptr(cdata->cfg.setting),
+ (void __user *)compat_ptr(cdata->cfg.setting),
sizeof(struct msm_camera_i2c_reg_setting32))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -525,7 +525,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
break;
}
if (copy_from_user(reg_setting,
- (void *)(conf_array.reg_setting),
+ (void __user *)(conf_array.reg_setting),
conf_array.size *
sizeof(struct msm_camera_i2c_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -571,7 +571,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
(struct msm_camera_i2c_read_config *)
compat_ptr(cdata->cfg.setting);
- if (copy_from_user(&read_config, read_config_ptr,
+ if (copy_from_user(&read_config, (void __user *)read_config_ptr,
sizeof(struct msm_camera_i2c_read_config))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -640,7 +640,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
goto DONE;
if (copy_from_user(&write_config32,
- (void *)compat_ptr(cdata->cfg.setting),
+ (void __user *)compat_ptr(cdata->cfg.setting),
sizeof(
struct msm_camera_i2c_array_write_config32))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -682,7 +682,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
break;
}
if (copy_from_user(reg_setting,
- (void *)(write_config.conf_array.reg_setting),
+ (void __user *)(write_config.conf_array.reg_setting),
write_config.conf_array.size *
sizeof(struct msm_camera_i2c_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -753,7 +753,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
}
if (copy_from_user(&conf_array32,
- (void *)compat_ptr(cdata->cfg.setting),
+ (void __user *)compat_ptr(cdata->cfg.setting),
sizeof(struct msm_camera_i2c_seq_reg_setting32))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -780,7 +780,8 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
rc = -ENOMEM;
break;
}
- if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ if (copy_from_user(reg_setting,
+ (void __user *)conf_array.reg_setting,
conf_array.size *
sizeof(struct msm_camera_i2c_seq_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -863,7 +864,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
goto DONE;
if (copy_from_user(&stop_setting32,
- (void *)compat_ptr((cdata->cfg.setting)),
+ (void __user *)compat_ptr((cdata->cfg.setting)),
sizeof(struct msm_camera_i2c_reg_setting32))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -890,7 +891,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
break;
}
if (copy_from_user(stop_setting->reg_setting,
- (void *)reg_setting,
+ (void __user *)reg_setting,
stop_setting->size *
sizeof(struct msm_camera_i2c_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -949,7 +950,7 @@ DONE:
}
#endif
-int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
+int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp)
{
struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
int32_t rc = 0;
@@ -1026,7 +1027,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
}
if (copy_from_user(&conf_array,
- (void *)cdata->cfg.setting,
+ (void __user *)cdata->cfg.setting,
sizeof(struct msm_camera_i2c_reg_setting))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -1046,7 +1047,8 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
rc = -ENOMEM;
break;
}
- if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ if (copy_from_user(reg_setting,
+ (void __user *)conf_array.reg_setting,
conf_array.size *
sizeof(struct msm_camera_i2c_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -1089,7 +1091,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
read_config_ptr =
(struct msm_camera_i2c_read_config *)cdata->cfg.setting;
- if (copy_from_user(&read_config, read_config_ptr,
+ if (copy_from_user(&read_config, (void __user *)read_config_ptr,
sizeof(struct msm_camera_i2c_read_config))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -1153,7 +1155,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
goto DONE;
if (copy_from_user(&write_config,
- (void *)cdata->cfg.setting,
+ (void __user *)cdata->cfg.setting,
sizeof(struct msm_camera_i2c_array_write_config))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -1178,7 +1180,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
break;
}
if (copy_from_user(reg_setting,
- (void *)(write_config.conf_array.reg_setting),
+ (void __user *)(write_config.conf_array.reg_setting),
write_config.conf_array.size *
sizeof(struct msm_camera_i2c_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -1243,7 +1245,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
}
if (copy_from_user(&conf_array,
- (void *)cdata->cfg.setting,
+ (void __user *)cdata->cfg.setting,
sizeof(struct msm_camera_i2c_seq_reg_setting))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -1265,7 +1267,8 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
rc = -ENOMEM;
break;
}
- if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ if (copy_from_user(reg_setting,
+ (void __user *)conf_array.reg_setting,
conf_array.size *
sizeof(struct msm_camera_i2c_seq_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -1349,7 +1352,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
goto DONE;
if (copy_from_user(stop_setting,
- (void *)cdata->cfg.setting,
+ (void __user *)cdata->cfg.setting,
sizeof(struct msm_camera_i2c_reg_setting))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -1371,7 +1374,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
break;
}
if (copy_from_user(stop_setting->reg_setting,
- (void *)reg_setting,
+ (void __user *)reg_setting,
stop_setting->size *
sizeof(struct msm_camera_i2c_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.h b/drivers/media/platform/msm/ais/sensor/msm_sensor.h
index 060383b05170..eacd3b05420c 100644
--- a/drivers/media/platform/msm/ais/sensor/msm_sensor.h
+++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.h
@@ -94,7 +94,7 @@ struct msm_sensor_ctrl_t {
struct msm_sensor_init_t s_init;
};
-int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp);
+int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp);
int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl);
diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c
index 80c15717325c..c02972e5e993 100644
--- a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c
@@ -385,7 +385,7 @@ static int32_t msm_sensor_get_pw_settings_compat(
pr_err("failed: no memory ps32");
return -ENOMEM;
}
- if (copy_from_user(ps32, (void *)us_ps, sizeof(*ps32) * size)) {
+ if (copy_from_user(ps32, (void __user *)us_ps, sizeof(*ps32) * size)) {
pr_err("failed: copy_from_user");
kfree(ps32);
return -EFAULT;
@@ -413,22 +413,18 @@ static int32_t msm_sensor_create_pd_settings(void *setting,
#ifdef CONFIG_COMPAT
if (is_compat_task()) {
- int i = 0;
- struct msm_sensor_power_setting32 *power_setting_iter =
- (struct msm_sensor_power_setting32 *)compat_ptr((
- (struct msm_camera_sensor_slave_info32 *)setting)->
- power_setting_array.power_setting);
-
- for (i = 0; i < size_down; i++) {
- pd[i].config_val = power_setting_iter[i].config_val;
- pd[i].delay = power_setting_iter[i].delay;
- pd[i].seq_type = power_setting_iter[i].seq_type;
- pd[i].seq_val = power_setting_iter[i].seq_val;
+ rc = msm_sensor_get_pw_settings_compat(
+ pd, pu, size_down);
+ if (rc < 0) {
+ pr_err("failed");
+ return -EFAULT;
}
} else
#endif
{
- if (copy_from_user(pd, (void *)pu, sizeof(*pd) * size_down)) {
+ if (copy_from_user(pd,
+ (void __user *)pu,
+ sizeof(*pd) * size_down)) {
pr_err("failed: copy_from_user");
return -EFAULT;
}
@@ -480,7 +476,8 @@ static int32_t msm_sensor_get_power_down_settings(void *setting,
}
} else
#endif
- if (copy_from_user(pd, (void *)slave_info->power_setting_array.
+ if (copy_from_user(pd,
+ (void __user *)slave_info->power_setting_array.
power_down_setting, sizeof(*pd) * size_down)) {
pr_err("failed: copy_from_user");
kfree(pd);
@@ -546,7 +543,8 @@ static int32_t msm_sensor_get_power_up_settings(void *setting,
#endif
{
if (copy_from_user(pu,
- (void *)slave_info->power_setting_array.power_setting,
+ (void __user *)
+ slave_info->power_setting_array.power_setting,
sizeof(*pu) * size)) {
pr_err("failed: copy_from_user");
kfree(pu);
@@ -659,7 +657,7 @@ int32_t msm_sensor_driver_probe(void *setting,
rc = -ENOMEM;
goto free_slave_info;
}
- if (copy_from_user((void *)slave_info32, setting,
+ if (copy_from_user((void *)slave_info32, (void __user *)setting,
sizeof(*slave_info32))) {
pr_err("failed: copy_from_user");
rc = -EFAULT;
@@ -710,7 +708,7 @@ int32_t msm_sensor_driver_probe(void *setting,
#endif
{
if (copy_from_user(slave_info,
- (void *)setting, sizeof(*slave_info))) {
+ (void __user *)setting, sizeof(*slave_info))) {
pr_err("failed: copy_from_user");
rc = -EFAULT;
goto free_slave_info;
diff --git a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c
index f3147b127438..28a5402a4359 100644
--- a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c
+++ b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c
@@ -382,7 +382,7 @@ static int32_t msm_ois_control(struct msm_ois_ctrl_t *o_ctrl,
return -EFAULT;
}
if (copy_from_user(settings,
- (void *)set_info->ois_params.settings,
+ (void __user *)set_info->ois_params.settings,
set_info->ois_params.setting_size *
sizeof(struct reg_settings_ois_t))) {
kfree(settings);
@@ -407,7 +407,7 @@ static int32_t msm_ois_control(struct msm_ois_ctrl_t *o_ctrl,
static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl,
- void __user *argp)
+ void *argp)
{
struct msm_ois_cfg_data *cdata =
(struct msm_ois_cfg_data *)argp;
@@ -449,7 +449,7 @@ static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl,
} else
#endif
if (copy_from_user(&conf_array,
- (void *)cdata->cfg.settings,
+ (void __user *)cdata->cfg.settings,
sizeof(struct msm_camera_i2c_seq_reg_setting))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
@@ -470,7 +470,8 @@ static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl,
rc = -ENOMEM;
break;
}
- if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ if (copy_from_user(reg_setting,
+ (void __user *)conf_array.reg_setting,
conf_array.size *
sizeof(struct msm_camera_i2c_seq_reg_array))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
@@ -495,7 +496,7 @@ static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl,
}
static int32_t msm_ois_config_download(struct msm_ois_ctrl_t *o_ctrl,
- void __user *argp)
+ void *argp)
{
struct msm_ois_cfg_download_data *cdata =
(struct msm_ois_cfg_download_data *)argp;
@@ -606,7 +607,7 @@ static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd,
{
int rc;
struct msm_ois_ctrl_t *o_ctrl = v4l2_get_subdevdata(sd);
- void __user *argp = (void __user *)arg;
+ void *argp = arg;
CDBG("Enter\n");
CDBG("%s:%d o_ctrl %pK argp %pK\n", __func__, __LINE__, o_ctrl, argp);
@@ -805,7 +806,7 @@ static long msm_ois_subdev_do_ioctl(
break;
case CFG_OIS_I2C_WRITE_SEQ_TABLE:
if (copy_from_user(&settings32,
- (void *)compat_ptr(u32->cfg.settings),
+ (void __user *)compat_ptr(u32->cfg.settings),
sizeof(
struct msm_camera_i2c_seq_reg_setting32))) {
pr_err("copy_from_user failed\n");
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
index 2030755d59d0..7d9edde62c1b 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
@@ -1360,11 +1360,12 @@ int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data)
/* set attributes */
ret = iommu_domain_set_attr(domain, cb->attr, (void *)data);
if (ret < 0) {
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
pr_err("Error: set attr\n");
return -ENODEV;
}
} else {
- return -EINVAL;
+ ret = -EINVAL;
}
mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
return ret;
diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
index c5f5a12d08eb..a04d7ca73fe1 100644
--- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
@@ -749,9 +749,13 @@ static int msm_fd_s_fmt_vid_out(struct file *file,
static int msm_fd_reqbufs(struct file *file,
void *fh, struct v4l2_requestbuffers *req)
{
+ int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- return vb2_reqbufs(&ctx->vb2_q, req);
+ mutex_lock(&ctx->fd_device->recovery_lock);
+ ret = vb2_reqbufs(&ctx->vb2_q, req);
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+ return ret;
}
/*
@@ -763,9 +767,14 @@ static int msm_fd_reqbufs(struct file *file,
static int msm_fd_qbuf(struct file *file, void *fh,
struct v4l2_buffer *pb)
{
+ int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- return vb2_qbuf(&ctx->vb2_q, pb);
+ mutex_lock(&ctx->fd_device->recovery_lock);
+ ret = vb2_qbuf(&ctx->vb2_q, pb);
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+ return ret;
+
}
/*
@@ -777,9 +786,13 @@ static int msm_fd_qbuf(struct file *file, void *fh,
static int msm_fd_dqbuf(struct file *file,
void *fh, struct v4l2_buffer *pb)
{
+ int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- return vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK);
+ mutex_lock(&ctx->fd_device->recovery_lock);
+ ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK);
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+ return ret;
}
/*
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
index 5dbed80f5b85..8f3cffb4c3da 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -1166,10 +1166,15 @@ int msm_isp_smmu_attach(struct msm_isp_buf_mgr *buf_mgr,
{
struct msm_vfe_smmu_attach_cmd *cmd = arg;
int rc = 0;
+ int32_t stall_disable = 1;
pr_debug("%s: cmd->security_mode : %d\n", __func__, cmd->security_mode);
+
mutex_lock(&buf_mgr->lock);
if (cmd->iommu_attach_mode == IOMMU_ATTACH) {
+ /* disable smmu stall on fault */
+ cam_smmu_set_attr(buf_mgr->iommu_hdl,
+ DOMAIN_ATTR_CB_STALL_DISABLE, &stall_disable);
/*
* Call hypervisor thru scm call to notify secure or
* non-secure mode
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index d64cee834bea..1f1435fe282e 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -456,7 +456,7 @@ struct msm_vfe_axi_stream {
uint32_t runtime_output_format;
enum msm_stream_rdi_input_type rdi_input_type;
struct msm_isp_sw_framskip sw_skip;
- uint8_t sw_ping_pong_bit;
+ int8_t sw_ping_pong_bit;
struct vfe_device *vfe_dev[MAX_VFE];
int num_isp;
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 f9809615b407..0e0f10e7fbb5 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
@@ -1259,7 +1259,7 @@ void msm_isp_get_avtimer_ts(
int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
- int rc = 0, i;
+ int rc = 0, i = 0;
uint32_t io_format = 0;
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg;
struct msm_vfe_axi_stream *stream_info;
@@ -1395,7 +1395,7 @@ done:
int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
- int rc = 0, i;
+ int rc = 0, i = 0;
struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg;
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_stream_cfg_cmd stream_cfg;
@@ -1699,6 +1699,9 @@ static int msm_isp_update_deliver_count(struct vfe_device *vfe_dev,
rc = -EINVAL;
goto done;
} else {
+ /*After wm reload, we get bufdone for ping buffer*/
+ if (stream_info->sw_ping_pong_bit == -1)
+ stream_info->sw_ping_pong_bit = 0;
stream_info->undelivered_request_cnt--;
if (pingpong_bit != stream_info->sw_ping_pong_bit) {
pr_err("%s:%d ping pong bit actual %d sw %d\n",
@@ -2296,6 +2299,8 @@ static void msm_isp_input_disable(struct vfe_device *vfe_dev, int cmd_type)
ms_res->src_info[src_info->dual_hw_ms_info.index] =
NULL;
ms_res->num_src--;
+ if (ms_res->num_src == 0)
+ ms_res->dual_sync_mode = MSM_ISP_DUAL_CAM_ASYNC;
src_info->dual_hw_ms_info.sync_state =
MSM_ISP_DUAL_CAM_ASYNC;
src_info->dual_hw_type = DUAL_NONE;
@@ -2480,6 +2485,12 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
rc = -1;
return rc;
}
+ if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) {
+ pr_err("%s: ab_ib_vote num_src is exceeding limit\n",
+ __func__);
+ rc = -1;
+ return rc;
+ }
if (ab_ib_vote->lpm_mode) {
for (i = 0; i < ab_ib_vote->num_src; i++) {
stream_info =
@@ -2492,7 +2503,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;
@@ -2512,7 +2524,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;
@@ -3469,7 +3482,14 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
spin_lock_irqsave(&stream_info->lock, flags);
vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
- if (stream_info->undelivered_request_cnt == 1) {
+ /*
+ * When wm reloaded, pingpong status register would be stale, pingpong
+ * status would be updated only after AXI_DONE interrupt processed.
+ * So, we should avoid reading value from pingpong status register
+ * until buf_done happens for ping buffer.
+ */
+ if ((stream_info->undelivered_request_cnt == 1) &&
+ (stream_info->sw_ping_pong_bit != -1)) {
pingpong_status =
vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(
vfe_dev);
@@ -3542,10 +3562,25 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
stream_info->vfe_dev[k]->vfe_base, wm_mask);
}
- stream_info->sw_ping_pong_bit = 0;
+ /*
+ * sw_ping_pong_bit is updated only when AXI_DONE.
+ * so now reset this bit to -1.
+ */
+ stream_info->sw_ping_pong_bit = -1;
} else if (stream_info->undelivered_request_cnt == 2) {
- rc = msm_isp_cfg_ping_pong_address(
- stream_info, pingpong_status);
+ if (stream_info->sw_ping_pong_bit == -1) {
+ /*
+ * This means wm is reloaded & ping buffer is
+ * already configured. And AXI_DONE for ping
+ * is still pending. So, config pong buffer
+ * now.
+ */
+ rc = msm_isp_cfg_ping_pong_address(stream_info,
+ VFE_PONG_FLAG);
+ } else {
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, pingpong_status);
+ }
if (rc) {
stream_info->undelivered_request_cnt--;
spin_unlock_irqrestore(&stream_info->lock,
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 24e3223a79d0..3f900ded090a 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -86,6 +86,12 @@ static void msm_ispif_io_dump_reg(struct ispif_device *ispif)
{
if (!ispif->enb_dump_reg)
return;
+
+ if (!ispif->base) {
+ pr_err("%s: null pointer for the ispif base\n", __func__);
+ return;
+ }
+
msm_camera_io_dump(ispif->base, 0x250, 0);
}
@@ -621,7 +627,7 @@ static int msm_ispif_reset(struct ispif_device *ispif)
ispif->base + ISPIF_VFE_m_INTF_CMD_0(i));
msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
ispif->base + ISPIF_VFE_m_INTF_CMD_1(i));
- pr_debug("%s: base %lx", __func__, (unsigned long)ispif->base);
+ pr_debug("%s: base %pK", __func__, ispif->base);
msm_camera_io_w(0, ispif->base +
ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0));
msm_camera_io_w(0, ispif->base +
@@ -1021,6 +1027,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/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
index cd48f871eb79..bc844bb87db5 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
@@ -573,7 +573,10 @@ static int32_t msm_actuator_move_focus(
CDBG("called, dir %d, num_steps %d\n", dir, num_steps);
- if (dest_step_pos == a_ctrl->curr_step_pos)
+ if ((dest_step_pos == a_ctrl->curr_step_pos) ||
+ ((dest_step_pos <= a_ctrl->total_steps) &&
+ (a_ctrl->step_position_table[dest_step_pos] ==
+ a_ctrl->step_position_table[a_ctrl->curr_step_pos])))
return rc;
if ((sign_dir > MSM_ACTUATOR_MOVE_SIGNED_NEAR) ||
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 75043e1b0427..7dda92510879 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -1602,6 +1602,12 @@ static int32_t msm_cci_write(struct v4l2_subdev *sd,
return rc;
}
+ if (cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid cci state %d\n",
+ __func__, cci_dev->cci_state);
+ return -EINVAL;
+ }
+
if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
|| c_ctrl->cci_info->cci_i2c_master < 0) {
pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
index 5376e1e4b6a4..491b8d31935a 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-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
@@ -1144,13 +1144,13 @@ static long msm_flash_subdev_do_ioctl(
sd = vdev_to_v4l2_subdev(vdev);
u32 = (struct msm_flash_cfg_data_t32 *)arg;
- flash_data.cfg_type = u32->cfg_type;
- for (i = 0; i < MAX_LED_TRIGGERS; i++) {
- flash_data.flash_current[i] = u32->flash_current[i];
- flash_data.flash_duration[i] = u32->flash_duration[i];
- }
switch (cmd) {
case VIDIOC_MSM_FLASH_CFG32:
+ flash_data.cfg_type = u32->cfg_type;
+ for (i = 0; i < MAX_LED_TRIGGERS; i++) {
+ flash_data.flash_current[i] = u32->flash_current[i];
+ flash_data.flash_duration[i] = u32->flash_duration[i];
+ }
cmd = VIDIOC_MSM_FLASH_CFG;
switch (flash_data.cfg_type) {
case CFG_FLASH_OFF:
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
index b3e5dc7f9cb8..c5cdee1bf706 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -520,14 +520,16 @@ int32_t msm_camera_tz_i2c_power_up(
msm_camera_tz_get_ta_handle(),
sensor_id,
&sensor_secure);
- if (!rc && sensor_secure)
+ if (!rc && sensor_secure) {
/* Sensor validated by TA*/
sensor_info[sensor_id].ready++;
+ msm_camera_tz_unlock();
+ }
else {
+ msm_camera_tz_unlock();
msm_camera_tz_unload_ta();
rc = -EFAULT;
}
- msm_camera_tz_unlock();
}
} else
rc = -EFAULT;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index e1143c356721..fcef05374098 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -584,7 +584,12 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__);
break;
}
- read_config_ptr->data = local_data;
+ if (copy_to_user(&read_config_ptr->data,
+ &local_data, sizeof(local_data))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
break;
}
case CFG_SLAVE_WRITE_I2C_ARRAY: {
@@ -1098,7 +1103,12 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__);
break;
}
- read_config_ptr->data = local_data;
+ if (copy_to_user(&read_config_ptr->data,
+ &local_data, sizeof(local_data))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
break;
}
case CFG_SLAVE_WRITE_I2C_ARRAY: {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
index 891e528f75f1..a41d7dba490e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -469,17 +469,11 @@ static int32_t msm_sensor_create_pd_settings(void *setting,
#ifdef CONFIG_COMPAT
if (is_compat_task()) {
- int i = 0;
- struct msm_sensor_power_setting32 *power_setting_iter =
- (struct msm_sensor_power_setting32 *)compat_ptr((
- (struct msm_camera_sensor_slave_info32 *)setting)->
- power_setting_array.power_setting);
-
- for (i = 0; i < size_down; i++) {
- pd[i].config_val = power_setting_iter[i].config_val;
- pd[i].delay = power_setting_iter[i].delay;
- pd[i].seq_type = power_setting_iter[i].seq_type;
- pd[i].seq_val = power_setting_iter[i].seq_val;
+ rc = msm_sensor_get_pw_settings_compat(
+ pd, pu, size_down);
+ if (rc < 0) {
+ pr_err("failed");
+ return -EFAULT;
}
} else
#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
index c94ee509631f..bfb15846e73c 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
@@ -774,11 +774,10 @@ static long msm_ois_subdev_do_ioctl(
u32 = (struct msm_ois_cfg_data32 *)arg;
parg = arg;
- ois_data.cfgtype = u32->cfgtype;
-
switch (cmd) {
case VIDIOC_MSM_OIS_CFG32:
cmd = VIDIOC_MSM_OIS_CFG;
+ ois_data.cfgtype = u32->cfgtype;
switch (u32->cfgtype) {
case CFG_OIS_CONTROL:
@@ -812,7 +811,6 @@ static long msm_ois_subdev_do_ioctl(
settings.reg_setting =
compat_ptr(settings32.reg_setting);
- ois_data.cfgtype = u32->cfgtype;
ois_data.cfg.settings = &settings;
parg = &ois_data;
break;
diff --git a/drivers/media/platform/msm/sde/Kconfig b/drivers/media/platform/msm/sde/Kconfig
index 85f5f4257ddb..d2c2e90f9de9 100644
--- a/drivers/media/platform/msm/sde/Kconfig
+++ b/drivers/media/platform/msm/sde/Kconfig
@@ -15,3 +15,15 @@ config MSM_SDE_ROTATOR_EVTLOG_DEBUG
features to: Dump rotator registers during driver errors, panic
driver during fatal errors and enable some rotator driver logging
into an internal buffer (this avoids logging overhead).
+
+config MSM_SDE_HDMI_CEC
+ bool "QTI SDE HDMI CEC Driver"
+ depends on DRM_SDE_HDMI
+ select MEDIA_CEC_SUPPORT
+ select MEDIA_CEC_EDID
+ select MEDIA_CEC_NOTIFIER
+ ---help---
+ The HDMI CEC driver provides support to enable HDMI CEC features
+ which allows various audiovisual products to communicate using HDMI
+ CEC links. CEC is a protocol defined in HDMI spec which consists of
+ both low-level and high-level protocol definition.
diff --git a/drivers/media/platform/msm/sde/Makefile b/drivers/media/platform/msm/sde/Makefile
index fe55d5044c3b..f39000a15993 100644
--- a/drivers/media/platform/msm/sde/Makefile
+++ b/drivers/media/platform/msm/sde/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_MSM_SDE_ROTATOR) += rotator/
+obj-$(CONFIG_MSM_SDE_HDMI_CEC) += cec/
diff --git a/drivers/media/platform/msm/sde/cec/Makefile b/drivers/media/platform/msm/sde/cec/Makefile
new file mode 100644
index 000000000000..2a9c30980512
--- /dev/null
+++ b/drivers/media/platform/msm/sde/cec/Makefile
@@ -0,0 +1,3 @@
+obj-y := \
+ sde_hdmi_cec.o \
+ sde_hdmi_cec_util.o
diff --git a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c
new file mode 100644
index 000000000000..b3798e8a9d24
--- /dev/null
+++ b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c
@@ -0,0 +1,429 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/cec.h>
+#include <media/cec.h>
+#include <media/cec-edid.h>
+#include <media/cec-notifier.h>
+
+#include "sde_hdmi_cec_util.h"
+
+#define CEC_NAME "sde-hdmi-cec"
+
+/* CEC Register Definition */
+#define CEC_INTR_MASK (BIT(1) | BIT(3) | BIT(7))
+#define CEC_SUPPORTED_HW_VERSION 0x30000001
+
+#define HDMI_CEC_WR_RANGE (0x000002DC)
+#define HDMI_CEC_RD_RANGE (0x000002E0)
+#define HDMI_VERSION (0x000002E4)
+#define HDMI_CEC_CTRL (0x0000028C)
+#define HDMI_CEC_WR_DATA (0x00000290)
+#define HDMI_CEC_RETRANSMIT (0x00000294)
+#define HDMI_CEC_STATUS (0x00000298)
+#define HDMI_CEC_INT (0x0000029C)
+#define HDMI_CEC_ADDR (0x000002A0)
+#define HDMI_CEC_TIME (0x000002A4)
+#define HDMI_CEC_REFTIMER (0x000002A8)
+#define HDMI_CEC_RD_DATA (0x000002AC)
+#define HDMI_CEC_RD_FILTER (0x000002B0)
+#define HDMI_CEC_COMPL_CTL (0x00000360)
+#define HDMI_CEC_RD_START_RANGE (0x00000364)
+#define HDMI_CEC_RD_TOTAL_RANGE (0x00000368)
+#define HDMI_CEC_RD_ERR_RESP_LO (0x0000036C)
+#define HDMI_CEC_WR_CHECK_CONFIG (0x00000370)
+
+enum cec_irq_status {
+ CEC_IRQ_FRAME_WR_DONE = 1 << 0,
+ CEC_IRQ_FRAME_RD_DONE = 1 << 1,
+ CEC_IRQ_FRAME_ERROR = 1 << 2,
+};
+
+struct sde_hdmi_cec {
+ struct cec_adapter *adap;
+ struct device *dev;
+ struct cec_hw_resource hw_res;
+ int irq;
+ enum cec_irq_status irq_status;
+ struct cec_notifier *notifier;
+};
+
+static int sde_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct sde_hdmi_cec *cec = adap->priv;
+ struct cec_hw_resource *hw = &cec->hw_res;
+ u32 hdmi_hw_version, reg_val;
+
+ pr_debug("adap enable %d\n", enable);
+
+ if (enable) {
+ pm_runtime_get_sync(cec->dev);
+
+ /* 19.2Mhz * 0.00005 us = 950 = 0x3B6 */
+ CEC_REG_WRITE(hw, HDMI_CEC_REFTIMER, (0x3B6 & 0xFFF) | BIT(16));
+
+ hdmi_hw_version = CEC_REG_READ(hw, HDMI_VERSION);
+ if (hdmi_hw_version >= CEC_SUPPORTED_HW_VERSION) {
+ CEC_REG_WRITE(hw, HDMI_CEC_RD_RANGE, 0x30AB9888);
+ CEC_REG_WRITE(hw, HDMI_CEC_WR_RANGE, 0x888AA888);
+
+ CEC_REG_WRITE(hw, HDMI_CEC_RD_START_RANGE, 0x88888888);
+ CEC_REG_WRITE(hw, HDMI_CEC_RD_TOTAL_RANGE, 0x99);
+ CEC_REG_WRITE(hw, HDMI_CEC_COMPL_CTL, 0xF);
+ CEC_REG_WRITE(hw, HDMI_CEC_WR_CHECK_CONFIG, 0x4);
+ } else {
+ pr_err("CEC version %d is not supported.\n",
+ hdmi_hw_version);
+ return -EPERM;
+ }
+
+ CEC_REG_WRITE(hw, HDMI_CEC_RD_FILTER, BIT(0) | (0x7FF << 4));
+ CEC_REG_WRITE(hw, HDMI_CEC_TIME, BIT(0) | ((7 * 0x30) << 7));
+
+ /* Enable CEC interrupts */
+ CEC_REG_WRITE(hw, HDMI_CEC_INT, CEC_INTR_MASK);
+
+ /* Enable Engine */
+ CEC_REG_WRITE(hw, HDMI_CEC_CTRL, BIT(0));
+ } else {
+ /* Disable Engine */
+ CEC_REG_WRITE(hw, HDMI_CEC_CTRL, 0);
+
+ /* Disable CEC interrupts */
+ reg_val = CEC_REG_READ(hw, HDMI_CEC_INT);
+ CEC_REG_WRITE(hw, HDMI_CEC_INT, reg_val & ~CEC_INTR_MASK);
+
+ pm_runtime_put(cec->dev);
+ }
+
+ return 0;
+}
+
+static int sde_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct sde_hdmi_cec *cec = adap->priv;
+ struct cec_hw_resource *hw = &cec->hw_res;
+
+ pr_debug("set log addr %d\n", logical_addr);
+
+ CEC_REG_WRITE(hw, HDMI_CEC_ADDR, logical_addr & 0xF);
+
+ return 0;
+}
+
+static int sde_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct sde_hdmi_cec *cec = adap->priv;
+ struct cec_hw_resource *hw = &cec->hw_res;
+ u32 frame_type;
+ int i;
+ u32 line_check_retry = 10;
+
+ pr_debug("transmit msg [%d]->[%d]: len = %d, attampts=%d, signal_free_time=%d\n",
+ cec_msg_initiator(msg), cec_msg_destination(msg), msg->len,
+ attempts, signal_free_time);
+
+ /* toggle cec in order to flush out bad hw state, if any */
+ CEC_REG_WRITE(hw, HDMI_CEC_CTRL, 0);
+ CEC_REG_WRITE(hw, HDMI_CEC_CTRL, BIT(0));
+
+ /* make sure state is cleared */
+ wmb();
+
+ CEC_REG_WRITE(hw, HDMI_CEC_RETRANSMIT,
+ ((attempts & 0xF) << 4) | BIT(0));
+
+ frame_type = cec_msg_is_broadcast(msg) ? BIT(0) : 0;
+
+ /* header block */
+ CEC_REG_WRITE(hw, HDMI_CEC_WR_DATA,
+ (((cec_msg_initiator(msg) << 4) |
+ cec_msg_destination(msg)) << 8) | frame_type);
+
+ /* data block 0 : opcode */
+ CEC_REG_WRITE(hw, HDMI_CEC_WR_DATA,
+ ((msg->len < 2 ? 0 : cec_msg_opcode(msg)) << 8) | frame_type);
+
+ /* data block 1-14 : operand 0-13 */
+ for (i = 2; i < msg->len; i++)
+ CEC_REG_WRITE(hw, HDMI_CEC_WR_DATA,
+ (msg->msg[i] << 8) | frame_type);
+
+ /* check line status */
+ while ((CEC_REG_READ(hw, HDMI_CEC_STATUS) & BIT(0)) &&
+ line_check_retry) {
+ line_check_retry--;
+ pr_debug("CEC line is busy(%d)\n", line_check_retry);
+ schedule();
+ }
+
+ if (!line_check_retry && (CEC_REG_READ(hw, HDMI_CEC_STATUS) & BIT(0))) {
+ pr_err("CEC line is busy. Retry failed\n");
+ return -EBUSY;
+ }
+
+ /* start transmission */
+ CEC_REG_WRITE(hw, HDMI_CEC_CTRL, BIT(0) | BIT(1) |
+ ((msg->len & 0x1F) << 4) | BIT(9));
+
+ return 0;
+}
+
+static void sde_hdmi_cec_handle_rx_done(struct sde_hdmi_cec *cec)
+{
+ struct cec_hw_resource *hw = &cec->hw_res;
+ struct cec_msg msg = {};
+ u32 data;
+ int i;
+
+ pr_debug("rx done\n");
+
+ data = CEC_REG_READ(hw, HDMI_CEC_RD_DATA);
+ msg.len = (data & 0x1F00) >> 8;
+ if (msg.len < 1 || msg.len > CEC_MAX_MSG_SIZE) {
+ pr_err("invalid message size %d", msg.len);
+ return;
+ }
+ msg.msg[0] = data & 0xFF;
+
+ for (i = 1; i < msg.len; i++)
+ msg.msg[i] = CEC_REG_READ(hw, HDMI_CEC_RD_DATA) & 0xFF;
+
+ cec_received_msg(cec->adap, &msg);
+}
+
+static void sde_hdmi_cec_handle_tx_done(struct sde_hdmi_cec *cec)
+{
+ pr_debug("tx done\n");
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+}
+
+static void sde_hdmi_cec_handle_tx_error(struct sde_hdmi_cec *cec)
+{
+ struct cec_hw_resource *hw = &cec->hw_res;
+ u32 cec_status = CEC_REG_READ(hw, HDMI_CEC_STATUS);
+
+ pr_debug("tx error status %x\n", cec_status);
+
+ if ((cec_status & 0xF0) == 0x10)
+ cec_transmit_done(cec->adap,
+ CEC_TX_STATUS_NACK, 0, 1, 0, 0);
+ else if ((cec_status & 0xF0) == 0x30)
+ cec_transmit_done(cec->adap,
+ CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0);
+ else
+ cec_transmit_done(cec->adap,
+ CEC_TX_STATUS_ERROR | CEC_TX_STATUS_MAX_RETRIES,
+ 0, 0, 0, 1);
+}
+
+static irqreturn_t sde_hdmi_cec_irq_handler_thread(int irq, void *priv)
+{
+ struct sde_hdmi_cec *cec = priv;
+
+ pr_debug("irq thread: status %x\n", cec->irq_status);
+
+ if (cec->irq_status & CEC_IRQ_FRAME_WR_DONE)
+ sde_hdmi_cec_handle_tx_done(cec);
+
+ if (cec->irq_status & CEC_IRQ_FRAME_ERROR)
+ sde_hdmi_cec_handle_tx_error(cec);
+
+ if (cec->irq_status & CEC_IRQ_FRAME_RD_DONE)
+ sde_hdmi_cec_handle_rx_done(cec);
+
+ cec->irq_status = 0;
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sde_hdmi_cec_irq_handler(int irq, void *priv)
+{
+ struct sde_hdmi_cec *cec = priv;
+ struct cec_hw_resource *hw = &cec->hw_res;
+ u32 data = CEC_REG_READ(hw, HDMI_CEC_INT);
+
+ CEC_REG_WRITE(hw, HDMI_CEC_INT, data);
+
+ pr_debug("irq handler: %x\n", data);
+
+ if ((data & BIT(0)) && (data & BIT(1)))
+ cec->irq_status |= CEC_IRQ_FRAME_WR_DONE;
+
+ if ((data & BIT(2)) && (data & BIT(3)))
+ cec->irq_status |= CEC_IRQ_FRAME_ERROR;
+
+ if ((data & BIT(6)) && (data & BIT(7)))
+ cec->irq_status |= CEC_IRQ_FRAME_RD_DONE;
+
+ return cec->irq_status ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+static const struct cec_adap_ops sde_hdmi_cec_adap_ops = {
+ .adap_enable = sde_hdmi_cec_adap_enable,
+ .adap_log_addr = sde_hdmi_cec_adap_log_addr,
+ .adap_transmit = sde_hdmi_cec_adap_transmit,
+};
+
+static int sde_hdmi_cec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sde_hdmi_cec *cec;
+ struct device_node *np;
+ struct platform_device *hdmi_dev;
+ int ret;
+
+ cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ cec->dev = dev;
+
+ np = of_parse_phandle(pdev->dev.of_node, "qcom,hdmi-dev", 0);
+ if (!np) {
+ pr_err("failed to find hdmi node in device tree\n");
+ return -ENODEV;
+ }
+ hdmi_dev = of_find_device_by_node(np);
+ if (hdmi_dev == NULL)
+ return -EPROBE_DEFER;
+
+ cec->irq = of_irq_get(dev->of_node, 0);
+ if (cec->irq < 0) {
+ pr_err("failed to get irq\n");
+ return cec->irq;
+ }
+
+ ret = devm_request_threaded_irq(dev, cec->irq, sde_hdmi_cec_irq_handler,
+ sde_hdmi_cec_irq_handler_thread, 0,
+ pdev->name, cec);
+ if (ret)
+ return ret;
+
+ ret = sde_hdmi_cec_init_resource(pdev, &cec->hw_res);
+ if (ret)
+ return ret;
+
+ cec->adap = cec_allocate_adapter(&sde_hdmi_cec_adap_ops, cec,
+ CEC_NAME,
+ CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH |
+ CEC_CAP_TRANSMIT, 1);
+ ret = PTR_ERR_OR_ZERO(cec->adap);
+ if (ret)
+ return ret;
+
+ ret = cec_register_adapter(cec->adap, &pdev->dev);
+ if (ret)
+ goto err_del_adap;
+
+ cec->notifier = cec_notifier_get(&hdmi_dev->dev);
+ if (!cec->notifier) {
+ pr_err("failed to get cec notifier\n");
+ goto err_del_adap;
+ }
+
+ platform_set_drvdata(pdev, cec);
+
+ pm_runtime_enable(dev);
+
+ /*
+ * cec_register_cec_notifier has to be later than pm_runtime_enable
+ * because it calls adap_enable.
+ */
+ cec_register_cec_notifier(cec->adap, cec->notifier);
+
+ pr_debug("probe done\n");
+
+ return ret;
+
+err_del_adap:
+ cec_delete_adapter(cec->adap);
+ return ret;
+}
+
+static int sde_hdmi_cec_remove(struct platform_device *pdev)
+{
+ struct sde_hdmi_cec *cec = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ cec_unregister_adapter(cec->adap);
+ cec_notifier_put(cec->notifier);
+
+ devm_free_irq(&pdev->dev, cec->irq, cec);
+ sde_hdmi_cec_deinit_resource(pdev, &cec->hw_res);
+
+ pr_debug("remove done\n");
+
+ return 0;
+}
+
+static int __maybe_unused sde_hdmi_cec_runtime_suspend(struct device *dev)
+{
+ struct sde_hdmi_cec *cec = dev_get_drvdata(dev);
+ struct cec_hw_resource *hw = &cec->hw_res;
+
+ pr_debug("runtime suspend\n");
+
+ return sde_hdmi_cec_enable_power(hw, false);
+}
+
+static int __maybe_unused sde_hdmi_cec_runtime_resume(struct device *dev)
+{
+ struct sde_hdmi_cec *cec = dev_get_drvdata(dev);
+ struct cec_hw_resource *hw = &cec->hw_res;
+
+ pr_debug("runtime resume\n");
+
+ return sde_hdmi_cec_enable_power(hw, true);
+}
+
+static const struct dev_pm_ops sde_hdmi_cec_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(sde_hdmi_cec_runtime_suspend,
+ sde_hdmi_cec_runtime_resume, NULL)
+};
+
+static const struct of_device_id sde_hdmi_cec_match[] = {
+ {
+ .compatible = "qcom,hdmi-cec",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sde_hdmi_cec_match);
+
+static struct platform_driver sde_hdmi_cec_pdrv = {
+ .probe = sde_hdmi_cec_probe,
+ .remove = sde_hdmi_cec_remove,
+ .driver = {
+ .name = CEC_NAME,
+ .of_match_table = sde_hdmi_cec_match,
+ .pm = &sde_hdmi_cec_pm_ops,
+ },
+};
+
+module_platform_driver(sde_hdmi_cec_pdrv);
+MODULE_DESCRIPTION("MSM SDE HDMI CEC driver");
diff --git a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.c b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.c
new file mode 100644
index 000000000000..323e0805c886
--- /dev/null
+++ b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.c
@@ -0,0 +1,743 @@
+/* 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include "sde_hdmi_cec_util.h"
+
+void sde_hdmi_cec_reg_w(struct cec_io_data *io,
+ u32 offset, u32 value, bool debug)
+{
+ u32 in_val;
+
+ if (!io || !io->base) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ if (offset > io->len) {
+ pr_err("offset out of range\n");
+ return;
+ }
+
+ writel_relaxed(value, io->base + offset);
+ if (debug) {
+ in_val = readl_relaxed(io->base + offset);
+ pr_debug("[%08x] => %08x [%08x]\n",
+ (u32)(unsigned long)(io->base + offset),
+ value, in_val);
+ }
+}
+
+u32 sde_hdmi_cec_reg_r(struct cec_io_data *io, u32 offset, bool debug)
+{
+ u32 value;
+
+ if (!io || !io->base) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (offset > io->len) {
+ pr_err("offset out of range\n");
+ return -EINVAL;
+ }
+
+ value = readl_relaxed(io->base + offset);
+ if (debug)
+ pr_debug("[%08x] <= %08x\n",
+ (u32)(unsigned long)(io->base + offset), value);
+
+ return value;
+}
+
+void sde_hdmi_cec_reg_dump(void __iomem *base, u32 length, const char *prefix,
+ bool debug)
+{
+ if (debug)
+ print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 32, 4,
+ __io_virt(base), length, false);
+}
+
+static int sde_hdmi_cec_config_vreg(struct device *dev,
+ struct cec_vreg *in_vreg, int num_vreg, bool config)
+{
+ int i = 0, rc = 0;
+ struct cec_vreg *curr_vreg = NULL;
+ enum cec_vreg_type type;
+
+ if (!in_vreg || !num_vreg)
+ return rc;
+
+ if (config) {
+ for (i = 0; i < num_vreg; i++) {
+ curr_vreg = &in_vreg[i];
+ curr_vreg->vreg = regulator_get(dev,
+ curr_vreg->vreg_name);
+ rc = PTR_RET(curr_vreg->vreg);
+ if (rc) {
+ pr_err("%s get failed. rc=%d\n",
+ curr_vreg->vreg_name, rc);
+ curr_vreg->vreg = NULL;
+ goto vreg_get_fail;
+ }
+ type = (regulator_count_voltages(curr_vreg->vreg) > 0)
+ ? CEC_REG_LDO : CEC_REG_VS;
+ if (type == CEC_REG_LDO) {
+ rc = regulator_set_voltage(
+ curr_vreg->vreg,
+ curr_vreg->min_voltage,
+ curr_vreg->max_voltage);
+ if (rc < 0) {
+ pr_err("%s set vltg fail\n",
+ curr_vreg->vreg_name);
+ goto vreg_set_voltage_fail;
+ }
+ }
+ }
+ } else {
+ for (i = num_vreg-1; i >= 0; i--) {
+ curr_vreg = &in_vreg[i];
+ if (curr_vreg->vreg) {
+ type = (regulator_count_voltages(
+ curr_vreg->vreg) > 0)
+ ? CEC_REG_LDO : CEC_REG_VS;
+ if (type == CEC_REG_LDO) {
+ regulator_set_voltage(curr_vreg->vreg,
+ 0, curr_vreg->max_voltage);
+ }
+ regulator_put(curr_vreg->vreg);
+ curr_vreg->vreg = NULL;
+ }
+ }
+ }
+ return 0;
+
+vreg_unconfig:
+if (type == CEC_REG_LDO)
+ regulator_set_load(curr_vreg->vreg, 0);
+
+vreg_set_voltage_fail:
+ regulator_put(curr_vreg->vreg);
+ curr_vreg->vreg = NULL;
+
+vreg_get_fail:
+ for (i--; i >= 0; i--) {
+ curr_vreg = &in_vreg[i];
+ type = (regulator_count_voltages(curr_vreg->vreg) > 0)
+ ? CEC_REG_LDO : CEC_REG_VS;
+ goto vreg_unconfig;
+ }
+ return rc;
+}
+
+static int sde_hdmi_cec_enable_vreg(struct cec_hw_resource *hw, int enable)
+{
+ int i = 0, rc = 0;
+ bool need_sleep;
+ struct cec_vreg *in_vreg = hw->vreg_config;
+ int num_vreg = hw->num_vreg;
+
+ if (enable) {
+ for (i = 0; i < num_vreg; i++) {
+ rc = PTR_RET(in_vreg[i].vreg);
+ if (rc) {
+ pr_err("%s regulator error. rc=%d\n",
+ in_vreg[i].vreg_name, rc);
+ goto vreg_set_opt_mode_fail;
+ }
+ need_sleep = !regulator_is_enabled(in_vreg[i].vreg);
+ if (in_vreg[i].pre_on_sleep && need_sleep)
+ usleep_range(in_vreg[i].pre_on_sleep * 1000,
+ in_vreg[i].pre_on_sleep * 1000);
+ rc = regulator_set_load(in_vreg[i].vreg,
+ in_vreg[i].enable_load);
+ if (rc < 0) {
+ pr_err("%s set opt m fail\n",
+ in_vreg[i].vreg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ rc = regulator_enable(in_vreg[i].vreg);
+ if (in_vreg[i].post_on_sleep && need_sleep)
+ usleep_range(in_vreg[i].post_on_sleep * 1000,
+ in_vreg[i].post_on_sleep * 1000);
+ if (rc < 0) {
+ pr_err("%s enable failed\n",
+ in_vreg[i].vreg_name);
+ goto disable_vreg;
+ }
+ }
+ } else {
+ for (i = num_vreg-1; i >= 0; i--) {
+ if (in_vreg[i].pre_off_sleep)
+ usleep_range(in_vreg[i].pre_off_sleep * 1000,
+ in_vreg[i].pre_off_sleep * 1000);
+ regulator_set_load(in_vreg[i].vreg,
+ in_vreg[i].disable_load);
+ regulator_disable(in_vreg[i].vreg);
+ if (in_vreg[i].post_off_sleep)
+ usleep_range(in_vreg[i].post_off_sleep * 1000,
+ in_vreg[i].post_off_sleep * 1000);
+ }
+ }
+ return rc;
+
+disable_vreg:
+ regulator_set_load(in_vreg[i].vreg, in_vreg[i].disable_load);
+
+vreg_set_opt_mode_fail:
+ for (i--; i >= 0; i--) {
+ if (in_vreg[i].pre_off_sleep)
+ usleep_range(in_vreg[i].pre_off_sleep * 1000,
+ in_vreg[i].pre_off_sleep * 1000);
+ regulator_set_load(in_vreg[i].vreg,
+ in_vreg[i].disable_load);
+ regulator_disable(in_vreg[i].vreg);
+ if (in_vreg[i].post_off_sleep)
+ usleep_range(in_vreg[i].post_off_sleep * 1000,
+ in_vreg[i].post_off_sleep * 1000);
+ }
+
+ return rc;
+}
+
+static void sde_hdmi_cec_put_clk(struct cec_clk *clk_arry, int num_clk)
+{
+ int i;
+
+ for (i = num_clk - 1; i >= 0; i--) {
+ if (clk_arry[i].clk)
+ clk_put(clk_arry[i].clk);
+ clk_arry[i].clk = NULL;
+ }
+}
+
+static int sde_hdmi_cec_get_clk(struct device *dev,
+ struct cec_clk *clk_arry, int num_clk)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < num_clk; i++) {
+ clk_arry[i].clk = clk_get(dev, clk_arry[i].clk_name);
+ rc = PTR_RET(clk_arry[i].clk);
+ if (rc) {
+ pr_err("'%s' get failed. rc=%d\n",
+ clk_arry[i].clk_name, rc);
+ goto error;
+ }
+ }
+
+ return rc;
+
+error:
+ sde_hdmi_cec_put_clk(clk_arry, num_clk);
+
+ return rc;
+}
+
+static int sde_hdmi_cec_enable_clk(struct cec_hw_resource *hw, int enable)
+{
+ int i, rc = 0;
+ struct cec_clk *clk_arry = hw->clk_config;
+ int num_clk = hw->num_clk;
+
+ if (enable) {
+ for (i = 0; i < num_clk; i++) {
+ pr_debug("enable %s\n", clk_arry[i].clk_name);
+ if (clk_arry[i].clk) {
+ rc = clk_prepare_enable(clk_arry[i].clk);
+ if (rc)
+ pr_err("%s enable fail. rc=%d\n",
+ clk_arry[i].clk_name, rc);
+ } else {
+ pr_err("%s is not available\n",
+ clk_arry[i].clk_name);
+ rc = -EPERM;
+ }
+ }
+ } else {
+ for (i = num_clk - 1; i >= 0; i--) {
+ pr_debug("disable %s\n", clk_arry[i].clk_name);
+
+ if (clk_arry[i].clk)
+ clk_disable_unprepare(clk_arry[i].clk);
+ else
+ pr_err("%s is not available\n",
+ clk_arry[i].clk_name);
+ }
+ }
+
+ return rc;
+}
+
+static int sde_hdmi_cec_pinctrl_enable(struct cec_hw_resource *hw,
+ bool enable)
+{
+ struct pinctrl_state *pin_state = NULL;
+ int rc = 0;
+
+ if (!hw) {
+ pr_err("invalid input param hw:%pK\n", hw);
+ return -EINVAL;
+ }
+
+ pr_debug("set cec pinctrl state %d\n", enable);
+
+ pin_state = enable ? hw->pin_res.state_active : hw->pin_res.state_sleep;
+
+ if (!IS_ERR_OR_NULL(hw->pin_res.pinctrl))
+ rc = pinctrl_select_state(hw->pin_res.pinctrl,
+ pin_state);
+ else
+ pr_err("pinstate not found\n");
+
+ return rc;
+}
+
+static void sde_hdmi_cec_put_dt_clock(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ if (!pdev || !hw) {
+ pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw);
+ return;
+ }
+
+ if (hw->clk_config) {
+ sde_hdmi_cec_put_clk(hw->clk_config, hw->num_clk);
+ devm_kfree(&pdev->dev, hw->clk_config);
+ hw->clk_config = NULL;
+ }
+ hw->num_clk = 0;
+
+ pr_debug("put dt clock\n");
+}
+
+static int sde_hdmi_cec_get_dt_clock(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ int i = 0;
+ int num_clk = 0;
+ const char *clock_name;
+ int rc = 0;
+
+ if (!pdev || !hw) {
+ pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw);
+ return -EINVAL;
+ }
+
+ hw->num_clk = 0;
+ num_clk = of_property_count_strings(pdev->dev.of_node, "clock-names");
+ if (num_clk <= 0) {
+ pr_debug("clocks are not defined\n");
+ return 0;
+ }
+
+ hw->num_clk = num_clk;
+ hw->clk_config = devm_kzalloc(&pdev->dev,
+ sizeof(struct cec_clk) * num_clk, GFP_KERNEL);
+ if (!hw->clk_config) {
+ hw->num_clk = 0;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_clk; i++) {
+ of_property_read_string_index(pdev->dev.of_node, "clock-names",
+ i, &clock_name);
+ strlcpy(hw->clk_config[i].clk_name, clock_name,
+ sizeof(hw->clk_config[i].clk_name));
+ }
+
+ rc = sde_hdmi_cec_get_clk(&pdev->dev, hw->clk_config, hw->num_clk);
+ if (rc) {
+ sde_hdmi_cec_put_dt_clock(pdev, hw);
+ return rc;
+ }
+
+ pr_debug("get dt clock\n");
+
+ return 0;
+}
+
+static int sde_hdmi_cec_get_dt_supply(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ int i = 0, rc = 0;
+ u32 tmp = 0;
+ struct device_node *of_node = NULL, *supply_root_node = NULL;
+ struct device_node *supply_node = NULL;
+
+ if (!pdev || !hw) {
+ pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw);
+ return -EINVAL;
+ }
+
+ of_node = pdev->dev.of_node;
+
+ hw->num_vreg = 0;
+ supply_root_node = of_get_child_by_name(of_node,
+ "qcom,platform-supply-entries");
+ if (!supply_root_node) {
+ pr_debug("no supply entry present\n");
+ return rc;
+ }
+
+ hw->num_vreg = of_get_available_child_count(supply_root_node);
+ if (hw->num_vreg == 0) {
+ pr_debug("no vreg present\n");
+ return rc;
+ }
+
+ pr_debug("vreg found. count=%d\n", hw->num_vreg);
+ hw->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct cec_vreg) *
+ hw->num_vreg, GFP_KERNEL);
+ if (!hw->vreg_config) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ for_each_available_child_of_node(supply_root_node, supply_node) {
+ const char *st = NULL;
+
+ rc = of_property_read_string(supply_node,
+ "qcom,supply-name", &st);
+ if (rc) {
+ pr_err("error reading name. rc=%d\n", rc);
+ goto error;
+ }
+
+ strlcpy(hw->vreg_config[i].vreg_name, st,
+ sizeof(hw->vreg_config[i].vreg_name));
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-min-voltage", &tmp);
+ if (rc) {
+ pr_err("error reading min volt. rc=%d\n", rc);
+ goto error;
+ }
+ hw->vreg_config[i].min_voltage = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-max-voltage", &tmp);
+ if (rc) {
+ pr_err("error reading max volt. rc=%d\n", rc);
+ goto error;
+ }
+ hw->vreg_config[i].max_voltage = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-enable-load", &tmp);
+ if (rc) {
+ pr_err("error reading enable load. rc=%d\n", rc);
+ goto error;
+ }
+ hw->vreg_config[i].enable_load = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-disable-load", &tmp);
+ if (rc) {
+ pr_err("error reading disable load. rc=%d\n", rc);
+ goto error;
+ }
+ hw->vreg_config[i].disable_load = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-on-sleep", &tmp);
+ if (rc)
+ pr_debug("no supply pre sleep value. rc=%d\n", rc);
+
+ hw->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-off-sleep", &tmp);
+ if (rc)
+ pr_debug("no supply pre sleep value. rc=%d\n", rc);
+
+ hw->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-on-sleep", &tmp);
+ if (rc)
+ pr_debug("no supply post sleep value. rc=%d\n", rc);
+
+ hw->vreg_config[i].post_on_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-off-sleep", &tmp);
+ if (rc)
+ pr_debug("no supply post sleep value. rc=%d\n", rc);
+
+ hw->vreg_config[i].post_off_sleep = (!rc ? tmp : 0);
+
+ pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n",
+ hw->vreg_config[i].vreg_name,
+ hw->vreg_config[i].min_voltage,
+ hw->vreg_config[i].max_voltage,
+ hw->vreg_config[i].enable_load,
+ hw->vreg_config[i].disable_load,
+ hw->vreg_config[i].pre_on_sleep,
+ hw->vreg_config[i].post_on_sleep,
+ hw->vreg_config[i].pre_off_sleep,
+ hw->vreg_config[i].post_off_sleep);
+ ++i;
+
+ rc = 0;
+ }
+
+ rc = sde_hdmi_cec_config_vreg(&pdev->dev,
+ hw->vreg_config, hw->num_vreg, true);
+ if (rc)
+ goto error;
+
+ pr_debug("get dt supply\n");
+
+ return rc;
+
+error:
+ if (hw->vreg_config) {
+ devm_kfree(&pdev->dev, hw->vreg_config);
+ hw->vreg_config = NULL;
+ hw->num_vreg = 0;
+ }
+
+ return rc;
+}
+
+static void sde_hdmi_cec_put_dt_supply(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ if (!pdev || !hw) {
+ pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw);
+ return;
+ }
+
+ sde_hdmi_cec_config_vreg(&pdev->dev,
+ hw->vreg_config, hw->num_vreg, false);
+
+ if (hw->vreg_config) {
+ devm_kfree(&pdev->dev, hw->vreg_config);
+ hw->vreg_config = NULL;
+ }
+ hw->num_vreg = 0;
+
+ pr_debug("put dt supply\n");
+}
+
+static int sde_hdmi_cec_get_dt_pinres(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ if (!pdev || !hw) {
+ pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw);
+ return -EINVAL;
+ }
+
+ hw->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR_OR_NULL(hw->pin_res.pinctrl)) {
+ pr_err("failed to get pinctrl\n");
+ return PTR_ERR(hw->pin_res.pinctrl);
+ }
+
+ hw->pin_res.state_active =
+ pinctrl_lookup_state(hw->pin_res.pinctrl, "cec_active");
+ if (IS_ERR_OR_NULL(hw->pin_res.state_active))
+ pr_debug("cannot get active pinstate\n");
+
+ hw->pin_res.state_sleep =
+ pinctrl_lookup_state(hw->pin_res.pinctrl, "cec_sleep");
+ if (IS_ERR_OR_NULL(hw->pin_res.state_sleep))
+ pr_debug("cannot get sleep pinstate\n");
+
+ pr_debug("get dt pinres data\n");
+
+ return 0;
+}
+
+static void sde_hdmi_cec_put_dt_pinres(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ if (!pdev || !hw) {
+ pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw);
+ return;
+ }
+
+ if (!IS_ERR_OR_NULL(hw->pin_res.pinctrl))
+ devm_pinctrl_put(hw->pin_res.pinctrl);
+}
+
+static void sde_hdmi_cec_deinit_power(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ if (!pdev || !hw) {
+ pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw);
+ return;
+ }
+
+ sde_hdmi_cec_put_dt_supply(pdev, hw);
+ sde_hdmi_cec_put_dt_clock(pdev, hw);
+ sde_hdmi_cec_put_dt_pinres(pdev, hw);
+
+ pr_debug("put dt power data\n");
+}
+
+static int sde_hdmi_cec_init_power(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ int rc = 0;
+
+ if (!pdev || !hw) {
+ pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw);
+ return -EINVAL;
+ }
+
+ /* VREG */
+ rc = sde_hdmi_cec_get_dt_supply(pdev, hw);
+ if (rc) {
+ pr_err("get_dt_supply failed. rc=%d\n", rc);
+ goto error;
+ }
+
+ /* Clock */
+ rc = sde_hdmi_cec_get_dt_clock(pdev, hw);
+ if (rc) {
+ pr_err("get_dt_clock failed. rc=%d\n", rc);
+ goto error;
+ }
+
+ /* Pinctrl */
+ rc = sde_hdmi_cec_get_dt_pinres(pdev, hw);
+ if (rc) {
+ pr_err("get_dt_pinres failed. rc=%d\n", rc);
+ goto error;
+ }
+
+ pr_debug("get dt power data\n");
+
+ return rc;
+
+error:
+ sde_hdmi_cec_deinit_power(pdev, hw);
+ return rc;
+}
+
+static int sde_hdmi_cec_init_io(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ struct resource *res = NULL;
+ struct cec_io_data *io_data = NULL;
+ const char *reg_name;
+
+ if (!pdev || !hw) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(pdev->dev.of_node, "reg-names",
+ &reg_name)) {
+ pr_err("cec reg not defined\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, reg_name);
+ if (!res) {
+ pr_err("%s get_res_byname failed\n", reg_name);
+ return -ENODEV;
+ }
+
+ io_data = &hw->io_res;
+ io_data->len = (u32)resource_size(res);
+ io_data->base = ioremap(res->start, io_data->len);
+ if (!io_data->base) {
+ pr_err("%s ioremap failed\n", reg_name);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void sde_hdmi_cec_deinit_io(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ struct cec_io_data *io_data = NULL;
+
+ if (!pdev || !hw) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io_data = &hw->io_res;
+
+ if (io_data->base) {
+ iounmap(io_data->base);
+ io_data->base = NULL;
+ }
+ io_data->len = 0;
+}
+
+int sde_hdmi_cec_init_resource(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ int rc = 0;
+
+ /* power */
+ rc = sde_hdmi_cec_init_power(pdev, hw);
+ if (rc)
+ return rc;
+
+ /* io */
+ rc = sde_hdmi_cec_init_io(pdev, hw);
+ if (rc)
+ goto io_error;
+
+ pr_debug("cec init resource\n");
+
+ return rc;
+
+io_error:
+ sde_hdmi_cec_deinit_power(pdev, hw);
+ return rc;
+}
+
+void sde_hdmi_cec_deinit_resource(struct platform_device *pdev,
+ struct cec_hw_resource *hw)
+{
+ sde_hdmi_cec_deinit_power(pdev, hw);
+ sde_hdmi_cec_deinit_io(pdev, hw);
+
+ pr_debug("cec deinit resource\n");
+}
+
+int sde_hdmi_cec_enable_power(struct cec_hw_resource *hw, bool enable)
+{
+ int rc = 0;
+
+ rc = sde_hdmi_cec_enable_vreg(hw, enable);
+ if (rc)
+ return rc;
+
+ rc = sde_hdmi_cec_pinctrl_enable(hw, enable);
+ if (rc)
+ return rc;
+
+ rc = sde_hdmi_cec_enable_clk(hw, enable);
+ if (rc)
+ return rc;
+
+ pr_debug("cec power enable = %d\n", enable);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.h b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.h
new file mode 100644
index 000000000000..f92c43ea3288
--- /dev/null
+++ b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.h
@@ -0,0 +1,93 @@
+/* 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.
+ */
+
+#ifndef __SDE_HDMI_CEC_UTIL_H__
+#define __SDE_HDMI_CEC_UTIL_H__
+
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/types.h>
+
+#ifdef DEBUG
+#define CEC_REG_WRITE(hw, off, val) \
+ sde_hdmi_cec_reg_w(&(hw)->io_res, (off), (val), true)
+#define CEC_REG_READ(hw, off) \
+ sde_hdmi_cec_reg_r(&(hw)->io_res, (off), true)
+#else
+#define CEC_REG_WRITE(hw, off, val) \
+ sde_hdmi_cec_reg_w(&(hw)->io_res, (off), (val), false)
+#define CEC_REG_READ(hw, off) \
+ sde_hdmi_cec_reg_r(&(hw)->io_res, (off), false)
+#endif
+
+struct cec_io_data {
+ u32 len;
+ void __iomem *base;
+};
+
+enum cec_vreg_type {
+ CEC_REG_LDO,
+ CEC_REG_VS,
+};
+
+struct cec_vreg {
+ struct regulator *vreg; /* vreg handle */
+ char vreg_name[32];
+ int min_voltage;
+ int max_voltage;
+ int enable_load;
+ int disable_load;
+ int pre_on_sleep;
+ int post_on_sleep;
+ int pre_off_sleep;
+ int post_off_sleep;
+};
+
+struct cec_clk {
+ struct clk *clk; /* clk handle */
+ char clk_name[32];
+};
+
+struct cec_pin_res {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *state_active;
+ struct pinctrl_state *state_sleep;
+};
+
+struct cec_hw_resource {
+ /* power */
+ unsigned num_vreg;
+ struct cec_vreg *vreg_config;
+ unsigned num_clk;
+ struct cec_clk *clk_config;
+ struct cec_pin_res pin_res;
+
+ /* io */
+ struct cec_io_data io_res;
+};
+
+void sde_hdmi_cec_reg_w(struct cec_io_data *io,
+ u32 offset, u32 value, bool debug);
+u32 sde_hdmi_cec_reg_r(struct cec_io_data *io, u32 offset, bool debug);
+void sde_hdmi_cec_reg_dump(void __iomem *base, u32 length, const char *prefix,
+ bool debug);
+
+int sde_hdmi_cec_init_resource(struct platform_device *pdev,
+ struct cec_hw_resource *hw);
+void sde_hdmi_cec_deinit_resource(struct platform_device *pdev,
+ struct cec_hw_resource *hw);
+int sde_hdmi_cec_enable_power(struct cec_hw_resource *hw, bool enable);
+
+#endif /* __SDE_HDMI_CEC_UTIL_H__ */
+
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index 62980f345f60..abf20aef1256 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -621,9 +621,10 @@ static int sde_rotator_secure_session_ctrl(bool enable)
if (mdata->wait_for_transition && mdata->secure_session_ctrl &&
mdata->callback_request) {
ret = mdata->wait_for_transition(mdata->sec_cam_en, enable);
- if (ret) {
+ if (ret < 0) {
SDEROT_ERR("failed Secure wait for transition %d\n",
ret);
+ ret = -EPERM;
} else {
if (mdata->sec_cam_en ^ enable) {
mdata->sec_cam_en = enable;
@@ -984,6 +985,7 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
{
int i, size, ret = 0;
char name[32];
+ struct sched_param param = { .sched_priority = 5 };
size = sizeof(struct sde_rot_queue) * mgr->queue_count;
mgr->commitq = devm_kzalloc(mgr->device, size, GFP_KERNEL);
@@ -994,11 +996,21 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
snprintf(name, sizeof(name), "rot_commitq_%d_%d",
mgr->device->id, i);
SDEROT_DBG("work queue name=%s\n", name);
- mgr->commitq[i].rot_work_queue =
- alloc_ordered_workqueue("%s",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
- if (!mgr->commitq[i].rot_work_queue) {
+ init_kthread_worker(&mgr->commitq[i].rot_kw);
+ mgr->commitq[i].rot_thread = kthread_run(kthread_worker_fn,
+ &mgr->commitq[i].rot_kw, name);
+ if (IS_ERR(mgr->commitq[i].rot_thread)) {
ret = -EPERM;
+ mgr->commitq[i].rot_thread = NULL;
+ break;
+ }
+
+ ret = sched_setscheduler(mgr->commitq[i].rot_thread,
+ SCHED_FIFO, &param);
+ if (ret) {
+ SDEROT_ERR(
+ "failed to set kthread priority for commitq %d\n",
+ ret);
break;
}
@@ -1015,10 +1027,21 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
snprintf(name, sizeof(name), "rot_doneq_%d_%d",
mgr->device->id, i);
SDEROT_DBG("work queue name=%s\n", name);
- mgr->doneq[i].rot_work_queue = alloc_ordered_workqueue("%s",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
- if (!mgr->doneq[i].rot_work_queue) {
+ init_kthread_worker(&mgr->doneq[i].rot_kw);
+ mgr->doneq[i].rot_thread = kthread_run(kthread_worker_fn,
+ &mgr->doneq[i].rot_kw, name);
+ if (IS_ERR(mgr->doneq[i].rot_thread)) {
ret = -EPERM;
+ mgr->doneq[i].rot_thread = NULL;
+ break;
+ }
+
+ ret = sched_setscheduler(mgr->doneq[i].rot_thread,
+ SCHED_FIFO, &param);
+ if (ret) {
+ SDEROT_ERR(
+ "failed to set kthread priority for doneq %d\n",
+ ret);
break;
}
@@ -1034,18 +1057,20 @@ static void sde_rotator_deinit_queue(struct sde_rot_mgr *mgr)
if (mgr->commitq) {
for (i = 0; i < mgr->queue_count; i++) {
- if (mgr->commitq[i].rot_work_queue)
- destroy_workqueue(
- mgr->commitq[i].rot_work_queue);
+ if (mgr->commitq[i].rot_thread) {
+ flush_kthread_worker(&mgr->commitq[i].rot_kw);
+ kthread_stop(mgr->commitq[i].rot_thread);
+ }
}
devm_kfree(mgr->device, mgr->commitq);
mgr->commitq = NULL;
}
if (mgr->doneq) {
for (i = 0; i < mgr->queue_count; i++) {
- if (mgr->doneq[i].rot_work_queue)
- destroy_workqueue(
- mgr->doneq[i].rot_work_queue);
+ if (mgr->doneq[i].rot_thread) {
+ flush_kthread_worker(&mgr->doneq[i].rot_kw);
+ kthread_stop(mgr->doneq[i].rot_thread);
+ }
}
devm_kfree(mgr->device, mgr->doneq);
mgr->doneq = NULL;
@@ -1090,6 +1115,8 @@ static int sde_rotator_assign_queue(struct sde_rot_mgr *mgr,
if (IS_ERR_OR_NULL(hw)) {
SDEROT_ERR("fail to allocate hw\n");
ret = PTR_ERR(hw);
+ if (!ret)
+ ret = -EINVAL;
} else {
queue->hw = hw;
}
@@ -1166,7 +1193,7 @@ void sde_rotator_queue_request(struct sde_rot_mgr *mgr,
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_QUEUE] = ktime_get();
- queue_work(queue->rot_work_queue, &entry->commit_work);
+ queue_kthread_work(&queue->rot_kw, &entry->commit_work);
}
}
@@ -1377,12 +1404,13 @@ static void sde_rotator_release_entry(struct sde_rot_mgr *mgr,
*
* Note this asynchronous handler is protected by hal lock.
*/
-static void sde_rotator_commit_handler(struct work_struct *work)
+static void sde_rotator_commit_handler(struct kthread_work *work)
{
struct sde_rot_entry *entry;
struct sde_rot_entry_container *request;
struct sde_rot_hw_resource *hw;
struct sde_rot_mgr *mgr;
+ struct sched_param param = { .sched_priority = 5 };
int ret;
entry = container_of(work, struct sde_rot_entry, commit_work);
@@ -1393,6 +1421,12 @@ static void sde_rotator_commit_handler(struct work_struct *work)
return;
}
+ ret = sched_setscheduler(entry->fenceq->rot_thread, SCHED_FIFO, &param);
+ if (ret) {
+ SDEROT_WARN("Fail to set kthread priority for fenceq: %d\n",
+ ret);
+ }
+
mgr = entry->private->mgr;
SDEROT_EVTLOG(
@@ -1466,7 +1500,7 @@ static void sde_rotator_commit_handler(struct work_struct *work)
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_FLUSH] = ktime_get();
- queue_work(entry->doneq->rot_work_queue, &entry->done_work);
+ queue_kthread_work(&entry->doneq->rot_kw, &entry->done_work);
sde_rot_mgr_unlock(mgr);
return;
error:
@@ -1478,8 +1512,8 @@ get_hw_res_err:
sde_rotator_release_entry(mgr, entry);
atomic_dec(&request->pending_count);
atomic_inc(&request->failed_count);
- if (request->retireq && request->retire_work)
- queue_work(request->retireq, request->retire_work);
+ if (request->retire_kw && request->retire_work)
+ queue_kthread_work(request->retire_kw, request->retire_work);
sde_rot_mgr_unlock(mgr);
}
@@ -1493,7 +1527,7 @@ get_hw_res_err:
*
* Note this asynchronous handler is protected by hal lock.
*/
-static void sde_rotator_done_handler(struct work_struct *work)
+static void sde_rotator_done_handler(struct kthread_work *work)
{
struct sde_rot_entry *entry;
struct sde_rot_entry_container *request;
@@ -1551,8 +1585,8 @@ static void sde_rotator_done_handler(struct work_struct *work)
ATRACE_INT("sde_rot_done", 1);
sde_rotator_release_entry(mgr, entry);
atomic_dec(&request->pending_count);
- if (request->retireq && request->retire_work)
- queue_work(request->retireq, request->retire_work);
+ if (request->retire_kw && request->retire_work)
+ queue_kthread_work(request->retire_kw, request->retire_work);
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_RETIRE] = ktime_get();
sde_rot_mgr_unlock(mgr);
@@ -1918,8 +1952,10 @@ static int sde_rotator_add_request(struct sde_rot_mgr *mgr,
entry->request = req;
- INIT_WORK(&entry->commit_work, sde_rotator_commit_handler);
- INIT_WORK(&entry->done_work, sde_rotator_done_handler);
+ init_kthread_work(&entry->commit_work,
+ sde_rotator_commit_handler);
+ init_kthread_work(&entry->done_work,
+ sde_rotator_done_handler);
SDEROT_DBG("Entry added. wbidx=%u, src{%u,%u,%u,%u}f=%u\n"
"dst{%u,%u,%u,%u}f=%u session_id=%u\n", item->wb_idx,
item->src_rect.x, item->src_rect.y,
@@ -1957,24 +1993,26 @@ static void sde_rotator_cancel_request(struct sde_rot_mgr *mgr,
struct sde_rot_entry *entry;
int i;
- /*
- * To avoid signal the rotation entry output fence in the wrong
- * order, all the entries in the same request needs to be canceled
- * first, before signaling the output fence.
- */
- SDEROT_DBG("cancel work start\n");
- sde_rot_mgr_unlock(mgr);
- for (i = req->count - 1; i >= 0; i--) {
- entry = req->entries + i;
- cancel_work_sync(&entry->commit_work);
- cancel_work_sync(&entry->done_work);
- }
- sde_rot_mgr_lock(mgr);
- SDEROT_DBG("cancel work done\n");
- for (i = req->count - 1; i >= 0; i--) {
- entry = req->entries + i;
- sde_rotator_signal_output(entry);
- sde_rotator_release_entry(mgr, entry);
+ if (atomic_read(&req->pending_count)) {
+ /*
+ * To avoid signal the rotation entry output fence in the wrong
+ * order, all the entries in the same request needs to be
+ * canceled first, before signaling the output fence.
+ */
+ SDEROT_DBG("cancel work start\n");
+ sde_rot_mgr_unlock(mgr);
+ for (i = req->count - 1; i >= 0; i--) {
+ entry = req->entries + i;
+ flush_kthread_worker(&entry->commitq->rot_kw);
+ flush_kthread_worker(&entry->doneq->rot_kw);
+ }
+ sde_rot_mgr_lock(mgr);
+ SDEROT_DBG("cancel work done\n");
+ for (i = req->count - 1; i >= 0; i--) {
+ entry = req->entries + i;
+ sde_rotator_signal_output(entry);
+ sde_rotator_release_entry(mgr, entry);
+ }
}
list_del_init(&req->list);
@@ -1999,7 +2037,7 @@ static void sde_rotator_free_completed_request(struct sde_rot_mgr *mgr,
list_for_each_entry_safe(req, req_next, &private->req_list, list) {
if ((atomic_read(&req->pending_count) == 0) &&
- (!req->retire_work && !req->retireq)) {
+ (!req->retire_work && !req->retire_kw)) {
list_del_init(&req->list);
devm_kfree(&mgr->pdev->dev, req);
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 2073c6d9f115..41918dd9b43e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/pm_runtime.h>
+#include <linux/kthread.h>
#include "sde_rotator_base.h"
#include "sde_rotator_util.h"
@@ -184,7 +185,8 @@ struct sde_rot_hw_resource {
};
struct sde_rot_queue {
- struct workqueue_struct *rot_work_queue;
+ struct kthread_worker rot_kw;
+ struct task_struct *rot_thread;
struct sde_rot_timeline *timeline;
struct sde_rot_hw_resource *hw;
};
@@ -195,8 +197,8 @@ struct sde_rot_entry_container {
u32 count;
atomic_t pending_count;
atomic_t failed_count;
- struct workqueue_struct *retireq;
- struct work_struct *retire_work;
+ struct kthread_worker *retire_kw;
+ struct kthread_work *retire_work;
struct sde_rot_entry *entries;
};
@@ -205,8 +207,8 @@ struct sde_rot_file_private;
struct sde_rot_entry {
struct sde_rotation_item item;
- struct work_struct commit_work;
- struct work_struct done_work;
+ struct kthread_work commit_work;
+ struct kthread_work done_work;
struct sde_rot_queue *commitq;
struct sde_rot_queue *fenceq;
struct sde_rot_queue *doneq;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index f41382b5b20c..1966fa9805c0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -1023,6 +1023,9 @@ static ssize_t sde_rotator_debug_base_offset_write(struct file *file,
if (sscanf(buf, "%5x %x", &off, &cnt) < 2)
return -EINVAL;
+ if (off % sizeof(u32))
+ return -EINVAL;
+
if (off > dbg->max_offset)
return -EINVAL;
@@ -1091,6 +1094,9 @@ static ssize_t sde_rotator_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;
@@ -1139,6 +1145,9 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
goto debug_read_error;
}
+ if (dbg->off % sizeof(u32))
+ return -EFAULT;
+
ptr = dbg->base + dbg->off;
tot = 0;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index cfee4efb6f16..08bbed147c86 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -53,8 +53,8 @@
#define SDE_ROTATOR_DEGREE_180 180
#define SDE_ROTATOR_DEGREE_90 90
-static void sde_rotator_submit_handler(struct work_struct *work);
-static void sde_rotator_retire_handler(struct work_struct *work);
+static void sde_rotator_submit_handler(struct kthread_work *work);
+static void sde_rotator_retire_handler(struct kthread_work *work);
#ifdef CONFIG_COMPAT
static long sde_rotator_compat_ioctl32(struct file *file,
unsigned int cmd, unsigned long arg);
@@ -466,8 +466,8 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q)
sde_rotator_cancel_all_requests(rot_dev->mgr, ctx->private);
sde_rot_mgr_unlock(rot_dev->mgr);
mutex_unlock(q->lock);
- cancel_work_sync(&ctx->submit_work);
- cancel_work_sync(&ctx->retire_work);
+ flush_kthread_work(&ctx->submit_work);
+ flush_kthread_work(&ctx->retire_work);
mutex_lock(q->lock);
}
@@ -480,7 +480,7 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q)
struct sde_rotator_vbinfo *vbinfo =
&ctx->vbinfo_cap[i];
- if (vbinfo->fence && vbinfo->fd < 0) {
+ if (vbinfo->fence) {
/* fence is not used */
SDEDEV_DBG(rot_dev->dev,
"put fence s:%d t:%d i:%d\n",
@@ -765,8 +765,6 @@ static ssize_t sde_rotator_ctx_show(struct kobject *kobj,
ctx->format_cap.fmt.pix.sizeimage);
SPRINT("abort_pending=%d\n", ctx->abort_pending);
SPRINT("command_pending=%d\n", atomic_read(&ctx->command_pending));
- SPRINT("submit_work=%d\n", work_busy(&ctx->submit_work));
- SPRINT("retire_work=%d\n", work_busy(&ctx->retire_work));
SPRINT("sequence=%u\n",
sde_rotator_get_timeline_commit_ts(ctx->work_queue.timeline));
SPRINT("timestamp=%u\n",
@@ -923,8 +921,8 @@ static int sde_rotator_open(struct file *file)
ctx->crop_out.width = 640;
ctx->crop_out.height = 480;
init_waitqueue_head(&ctx->wait_queue);
- INIT_WORK(&ctx->submit_work, sde_rotator_submit_handler);
- INIT_WORK(&ctx->retire_work, sde_rotator_retire_handler);
+ init_kthread_work(&ctx->submit_work, sde_rotator_submit_handler);
+ init_kthread_work(&ctx->retire_work, sde_rotator_retire_handler);
v4l2_fh_init(&ctx->fh, video);
file->private_data = &ctx->fh;
@@ -954,14 +952,16 @@ static int sde_rotator_open(struct file *file)
snprintf(name, sizeof(name), "rot_fenceq_%d_%d", rot_dev->dev->id,
ctx->session_id);
- ctx->work_queue.rot_work_queue = alloc_ordered_workqueue("%s",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
- if (!ctx->work_queue.rot_work_queue) {
- SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate workqueue\n");
+ init_kthread_worker(&ctx->work_queue.rot_kw);
+ ctx->work_queue.rot_thread = kthread_run(kthread_worker_fn,
+ &ctx->work_queue.rot_kw, name);
+ if (IS_ERR(ctx->work_queue.rot_thread)) {
+ SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate kthread\n");
ret = -EPERM;
+ ctx->work_queue.rot_thread = NULL;
goto error_alloc_workqueue;
}
- SDEDEV_DBG(ctx->rot_dev->dev, "work queue name=%s\n", name);
+ SDEDEV_DBG(ctx->rot_dev->dev, "kthread name=%s\n", name);
snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id,
ctx->session_id);
@@ -1010,7 +1010,8 @@ error_ctrl_handler:
error_open_session:
sde_rot_mgr_unlock(rot_dev->mgr);
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
- destroy_workqueue(ctx->work_queue.rot_work_queue);
+ flush_kthread_worker(&ctx->work_queue.rot_kw);
+ kthread_stop(ctx->work_queue.rot_thread);
error_alloc_workqueue:
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
error_create_sysfs:
@@ -1045,20 +1046,17 @@ static int sde_rotator_release(struct file *file)
v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
mutex_unlock(&rot_dev->lock);
- SDEDEV_DBG(rot_dev->dev, "release submit work s:%d w:%x\n",
- session_id, work_busy(&ctx->submit_work));
- cancel_work_sync(&ctx->submit_work);
+ SDEDEV_DBG(rot_dev->dev, "release submit work s:%d\n", session_id);
+ flush_kthread_worker(&ctx->work_queue.rot_kw);
SDEDEV_DBG(rot_dev->dev, "release session s:%d\n", session_id);
sde_rot_mgr_lock(rot_dev->mgr);
sde_rotator_session_close(rot_dev->mgr, ctx->private, session_id);
sde_rot_mgr_unlock(rot_dev->mgr);
- SDEDEV_DBG(rot_dev->dev, "release retire work s:%d w:%x\n",
- session_id, work_busy(&ctx->retire_work));
- cancel_work_sync(&ctx->retire_work);
+ SDEDEV_DBG(rot_dev->dev, "release retire work s:%d\n", session_id);
mutex_lock(&rot_dev->lock);
SDEDEV_DBG(rot_dev->dev, "release context s:%d\n", session_id);
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
- destroy_workqueue(ctx->work_queue.rot_work_queue);
+ kthread_stop(ctx->work_queue.rot_thread);
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
kobject_put(&ctx->kobj);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
@@ -1459,7 +1457,7 @@ static int sde_rotator_dqbuf(struct file *file,
&& (buf->index < ctx->nbuf_cap)) {
int idx = buf->index;
- if (ctx->vbinfo_cap[idx].fence && ctx->vbinfo_cap[idx].fd < 0) {
+ if (ctx->vbinfo_cap[idx].fence) {
/* fence is not used */
SDEDEV_DBG(ctx->rot_dev->dev, "put fence s:%d i:%d\n",
ctx->session_id, idx);
@@ -1787,6 +1785,7 @@ static long sde_rotator_private_ioctl(struct file *file, void *fh,
struct msm_sde_rotator_fence *fence = arg;
struct msm_sde_rotator_comp_ratio *comp_ratio = arg;
struct sde_rotator_vbinfo *vbinfo;
+ int ret;
switch (cmd) {
case VIDIOC_S_SDE_ROTATOR_FENCE:
@@ -1845,18 +1844,39 @@ static long sde_rotator_private_ioctl(struct file *file, void *fh,
vbinfo = &ctx->vbinfo_cap[fence->index];
- if (vbinfo->fence == NULL) {
- vbinfo->fd = -1;
- } else {
- vbinfo->fd =
- sde_rotator_get_sync_fence_fd(vbinfo->fence);
- if (vbinfo->fd < 0) {
+ if (!vbinfo)
+ return -EINVAL;
+
+ if (vbinfo->fence) {
+ ret = sde_rotator_get_sync_fence_fd(vbinfo->fence);
+ if (ret < 0) {
SDEDEV_ERR(rot_dev->dev,
"fail get fence fd s:%d\n",
ctx->session_id);
- return vbinfo->fd;
+ return ret;
}
+
+ /*
+ * Loose any reference to sync fence once we pass
+ * it to user. Driver does not clean up user
+ * unclosed fence descriptors.
+ */
+ vbinfo->fence = NULL;
+
+ /*
+ * Cache fence descriptor in case user calls this
+ * ioctl multiple times. Cached value would be stale
+ * if user duplicated and closed old descriptor.
+ */
+ vbinfo->fd = ret;
+ } else if (!sde_rotator_get_fd_sync_fence(vbinfo->fd)) {
+ /*
+ * User has closed cached fence descriptor.
+ * Invalidate descriptor cache.
+ */
+ vbinfo->fd = -1;
}
+
fence->fd = vbinfo->fd;
SDEDEV_DBG(rot_dev->dev,
@@ -2023,7 +2043,7 @@ static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = {
*
* This function is scheduled in work queue context.
*/
-static void sde_rotator_retire_handler(struct work_struct *work)
+static void sde_rotator_retire_handler(struct kthread_work *work)
{
struct vb2_v4l2_buffer *src_buf;
struct vb2_v4l2_buffer *dst_buf;
@@ -2209,7 +2229,7 @@ static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx,
goto error_init_request;
}
- req->retireq = ctx->work_queue.rot_work_queue;
+ req->retire_kw = &ctx->work_queue.rot_kw;
req->retire_work = &ctx->retire_work;
ret = sde_rotator_handle_request_common(
@@ -2238,7 +2258,7 @@ error_null_buffer:
*
* This function is scheduled in work queue context.
*/
-static void sde_rotator_submit_handler(struct work_struct *work)
+static void sde_rotator_submit_handler(struct kthread_work *work)
{
struct sde_rotator_ctx *ctx;
struct sde_rotator_device *rot_dev;
@@ -2325,7 +2345,7 @@ static void sde_rotator_device_run(void *priv)
/* disconnect request (will be freed by core layer) */
sde_rot_mgr_lock(rot_dev->mgr);
- ctx->request->retireq = NULL;
+ ctx->request->retire_kw = NULL;
ctx->request->retire_work = NULL;
ctx->request = NULL;
sde_rot_mgr_unlock(rot_dev->mgr);
@@ -2364,7 +2384,7 @@ static void sde_rotator_device_run(void *priv)
/* disconnect request (will be freed by core layer) */
sde_rot_mgr_lock(rot_dev->mgr);
- ctx->request->retireq = NULL;
+ ctx->request->retire_kw = NULL;
ctx->request->retire_work = NULL;
ctx->request = ERR_PTR(-EIO);
sde_rot_mgr_unlock(rot_dev->mgr);
@@ -2471,7 +2491,7 @@ static int sde_rotator_job_ready(void *priv)
v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx),
atomic_read(&ctx->command_pending));
atomic_inc(&ctx->command_pending);
- queue_work(ctx->work_queue.rot_work_queue, &ctx->submit_work);
+ queue_kthread_work(&ctx->work_queue.rot_kw, &ctx->submit_work);
} else if (!atomic_read(&ctx->request->pending_count)) {
/* if pending request completed, forward to device run state */
SDEDEV_DBG(rot_dev->dev,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
index c8dcdeee9ca0..8e4d86083508 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.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
@@ -21,6 +21,7 @@
#include <linux/iommu.h>
#include <linux/dma-buf.h>
#include <linux/msm-bus.h>
+#include <linux/kthread.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-ctrls.h>
@@ -131,8 +132,8 @@ struct sde_rotator_ctx {
struct sde_rotator_vbinfo *vbinfo_cap;
struct sde_rotator_vbinfo *vbinfo_out;
wait_queue_head_t wait_queue;
- struct work_struct submit_work;
- struct work_struct retire_work;
+ struct kthread_work submit_work;
+ struct kthread_work retire_work;
struct sde_rot_queue work_queue;
struct sde_rot_entry_container *request;
struct sde_rot_file_private *private;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
index 5b574ed9fabc..9e3d7d806a0c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.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
@@ -145,7 +145,7 @@ static struct sde_mdp_hw_resource *sde_rotator_hw_alloc(
struct sde_mdp_hw_resource *mdp_hw;
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
int pipe_ndx, offset = ctl_id;
- int ret;
+ int ret = 0;
mdp_hw = devm_kzalloc(&mgr->pdev->dev,
sizeof(struct sde_mdp_hw_resource), GFP_KERNEL);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index 8157e8641e60..acb697946e18 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -1257,6 +1257,7 @@ err_put:
data->srcp_dma_buf = NULL;
imap_err:
ion_free(rot->iclient, handle);
+ sde_smmu_ctrl(0);
return rc;
}
@@ -1270,8 +1271,19 @@ static int sde_hw_rotator_swts_map(struct sde_hw_rotator *rot)
{
int rc = 0;
struct sde_mdp_img_data *data = &rot->swts_buf;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
sde_smmu_ctrl(1);
+ if (mdata->wait_for_transition) {
+ rc = mdata->wait_for_transition(0, 0);
+ if (rc < 0) {
+ SDEROT_ERR("failed Secure wait for transition %d\n",
+ rc);
+ rc = -EPERM;
+ goto error;
+ }
+ }
+
rc = sde_smmu_map_dma_buf(data->srcp_dma_buf, data->srcp_table,
SDE_IOMMU_DOMAIN_ROT_UNSECURE, &data->addr,
&data->len, DMA_BIDIRECTIONAL);
@@ -1291,7 +1303,7 @@ static int sde_hw_rotator_swts_map(struct sde_hw_rotator *rot)
data->mapped = true;
SDEROT_DBG("swts buffer mapped: %pad/%lx va:%p\n", &data->addr,
- data->len, rot->swts_buffer);
+ data->len, rot->swts_buffer);
sde_smmu_ctrl(0);
return rc;
@@ -1301,6 +1313,8 @@ kmap_err:
err_unmap:
dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
DMA_FROM_DEVICE);
+error:
+ sde_smmu_ctrl(0);
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 7388dab92c34..037c6f3b12ab 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.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
@@ -1832,10 +1832,10 @@ int create_pkt_cmd_session_set_property(
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
- case HAL_PARAM_VENC_H264_GENERATE_AUDNAL:
+ case HAL_PARAM_VENC_GENERATE_AUDNAL:
{
create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL,
+ HFI_PROPERTY_PARAM_VENC_GENERATE_AUDNAL,
((struct hal_enable *)pdata)->enable);
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
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..de5a2dececdf 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -230,6 +230,14 @@ static int msm_v4l2_queryctrl(struct file *file, void *fh,
return msm_vidc_query_ctrl((void *)vidc_inst, ctrl);
}
+static int msm_v4l2_query_ext_ctrl(struct file *file, void *fh,
+ struct v4l2_query_ext_ctrl *ctrl)
+{
+ struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+ return msm_vidc_query_ext_ctrl((void *)vidc_inst, ctrl);
+}
+
static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
.vidioc_querycap = msm_v4l2_querycap,
.vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt,
@@ -247,6 +255,7 @@ static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
.vidioc_s_ctrl = msm_v4l2_s_ctrl,
.vidioc_g_ctrl = msm_v4l2_g_ctrl,
.vidioc_queryctrl = msm_v4l2_queryctrl,
+ .vidioc_query_ext_ctrl = msm_v4l2_query_ext_ctrl,
.vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl,
.vidioc_subscribe_event = msm_v4l2_subscribe_event,
.vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event,
@@ -321,6 +330,7 @@ static int msm_vidc_initialize_core(struct platform_device *pdev,
init_completion(&core->completions[i]);
}
+ msm_comm_sort_ctrl();
INIT_DELAYED_WORK(&core->fw_unload_work, msm_vidc_fw_unload_handler);
return rc;
}
@@ -766,7 +776,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 +787,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 953780e3c220..28a4006b480c 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -11,6 +11,7 @@
*
*/
+#include <linux/sort.h>
#include <linux/slab.h>
#include <soc/qcom/scm.h>
#include "msm_vidc_internal.h"
@@ -18,6 +19,7 @@
#include "vidc_hfi_api.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_dcvs.h"
+#include "msm_vdec.h"
#define MSM_VDEC_DVC_NAME "msm_vdec_8974"
#define MIN_NUM_OUTPUT_BUFFERS 4
@@ -553,6 +555,7 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
(1 << V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC)
),
.qmenu = mpeg_vidc_video_dpb_color_format,
+ .flags = V4L2_CTRL_FLAG_MODIFY_LAYOUT,
},
{
.id = V4L2_CID_VIDC_QBUF_MODE,
@@ -872,7 +875,7 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst,
dprintk(VIDC_ERR,
"Core %pK in bad state, ignoring prepare buf\n",
inst->core);
- goto exit;
+ return -EINVAL;
}
switch (b->type) {
@@ -925,7 +928,7 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst,
dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
break;
}
-exit:
+
return rc;
}
@@ -1767,6 +1770,7 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct msm_vidc_inst *inst;
int rc = 0;
struct hfi_device *hdev;
+ struct vb2_buffer *vb;
struct vb2_buf_entry *temp, *next;
if (!q || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q);
@@ -1777,6 +1781,14 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
+
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ inst->core->state == VIDC_CORE_INVALID ||
+ 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",
q->type, inst);
@@ -1791,8 +1803,7 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
break;
default:
dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type);
- rc = -EINVAL;
- goto stream_start_failed;
+ return -EINVAL;
}
if (rc) {
dprintk(VIDC_ERR,
@@ -1811,12 +1822,15 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
stream_start_failed:
if (rc) {
+ list_for_each_entry(vb, &q->queued_list, queued_entry) {
+ if (vb->type == q->type &&
+ vb->state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
+ }
mutex_lock(&inst->pendingq.lock);
- list_for_each_entry_safe(temp, next, &inst->pendingq.list,
- list) {
+ list_for_each_entry_safe(temp, next,
+ &inst->pendingq.list, list) {
if (temp->vb->type == q->type) {
- vb2_buffer_done(temp->vb,
- VB2_BUF_STATE_QUEUED);
list_del(&temp->list);
kfree(temp);
}
@@ -2224,6 +2238,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;
@@ -2278,12 +2293,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:
@@ -2791,3 +2817,21 @@ int msm_vdec_ctrl_init(struct msm_vidc_inst *inst)
return msm_comm_ctrl_init(inst, msm_vdec_ctrls,
ARRAY_SIZE(msm_vdec_ctrls), &msm_vdec_ctrl_ops);
}
+
+void msm_vdec_g_ctrl(struct msm_vidc_ctrl **ctrls, int *num_ctrls)
+{
+ *ctrls = msm_vdec_ctrls;
+ *num_ctrls = NUM_CTRLS;
+}
+
+static int msm_vdec_ctrl_cmp(const void *st1, const void *st2)
+{
+ return (int32_t)((struct msm_vidc_ctrl *)st1)->id -
+ (int32_t)((struct msm_vidc_ctrl *)st2)->id;
+}
+
+void msm_vdec_ctrl_sort(void)
+{
+ sort(msm_vdec_ctrls, NUM_CTRLS, sizeof(struct msm_vidc_ctrl),
+ msm_vdec_ctrl_cmp, NULL);
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.h b/drivers/media/platform/msm/vidc/msm_vdec.h
index 47426c143c08..227cc99242d8 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.h
+++ b/drivers/media/platform/msm/vidc/msm_vdec.h
@@ -18,12 +18,13 @@
int msm_vdec_inst_init(struct msm_vidc_inst *inst);
int msm_vdec_ctrl_init(struct msm_vidc_inst *inst);
-int msm_vdec_querycap(void *instance, struct v4l2_capability *cap);
-int msm_vdec_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
-int msm_vdec_s_fmt(void *instance, struct v4l2_format *f);
-int msm_vdec_g_fmt(void *instance, struct v4l2_format *f);
-int msm_vdec_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
-int msm_vdec_reqbufs(void *instance, struct v4l2_requestbuffers *b);
+int msm_vdec_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap);
+int msm_vdec_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f);
+int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f);
+int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f);
+int msm_vdec_s_ext_ctrl(struct msm_vidc_inst *inst,
+ struct v4l2_ext_controls *a);
+int msm_vdec_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b);
int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
int msm_vdec_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
@@ -32,6 +33,8 @@ int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec);
int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a);
-struct vb2_ops *msm_vdec_get_vb2q_ops(void);
+const struct vb2_ops *msm_vdec_get_vb2q_ops(void);
+void msm_vdec_g_ctrl(struct msm_vidc_ctrl **ctrls, int *num_ctrls);
+void msm_vdec_ctrl_sort(void);
#endif
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 30726354164b..c5816511e056 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -54,12 +54,12 @@
* 3x3 transformation matrix coefficients in s4.9 fixed point format
*/
static u32 vpe_csc_601_to_709_matrix_coeff[HAL_MAX_MATRIX_COEFFS] = {
- 470, 8170, 8148, 0, 490, 50, 0, 34, 483
+ 440, 8140, 8098, 0, 460, 52, 0, 34, 463
};
/* offset coefficients in s9 fixed point format */
static u32 vpe_csc_601_to_709_bias_coeff[HAL_MAX_BIAS_COEFFS] = {
- 34, 0, 4
+ 53, 0, 4
};
/* clamping value for Y/U/V([min,max] for Y/U/V) */
@@ -861,14 +861,14 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.step = 1,
},
{
- .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_AU_DELIMITER,
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER,
.name = "H264 AU Delimiter",
.type = V4L2_CTRL_TYPE_BOOLEAN,
- .minimum = V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED,
- .maximum = V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED,
+ .minimum = V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED,
+ .maximum = V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED,
.step = 1,
.default_value =
- V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED,
+ V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED,
},
{
.id = V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL,
@@ -1897,12 +1897,22 @@ static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct msm_vidc_inst *inst;
int rc = 0;
+ struct vb2_buffer *vb;
struct vb2_buf_entry *temp, *next;
+
if (!q || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q);
return -EINVAL;
}
inst = q->drv_priv;
+
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ inst->core->state == VIDC_CORE_INVALID ||
+ 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);
switch (q->type) {
@@ -1916,8 +1926,7 @@ static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count)
break;
default:
dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type);
- rc = -EINVAL;
- goto stream_start_failed;
+ return -EINVAL;
}
if (rc) {
dprintk(VIDC_ERR,
@@ -1936,12 +1945,15 @@ static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count)
stream_start_failed:
if (rc) {
+ list_for_each_entry(vb, &q->queued_list, queued_entry) {
+ if (vb->type == q->type &&
+ vb->state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
+ }
mutex_lock(&inst->pendingq.lock);
- list_for_each_entry_safe(temp, next, &inst->pendingq.list,
- list) {
+ list_for_each_entry_safe(temp, next,
+ &inst->pendingq.list, list) {
if (temp->vb->type == q->type) {
- vb2_buffer_done(temp->vb,
- VB2_BUF_STATE_QUEUED);
list_del(&temp->list);
kfree(temp);
}
@@ -3305,14 +3317,14 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &vui_timing_info;
break;
}
- case V4L2_CID_MPEG_VIDC_VIDEO_H264_AU_DELIMITER:
- property_id = HAL_PARAM_VENC_H264_GENERATE_AUDNAL;
+ case V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER:
+ property_id = HAL_PARAM_VENC_GENERATE_AUDNAL;
switch (ctrl->val) {
- case V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED:
+ case V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED:
enable.enable = 0;
break;
- case V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED:
+ case V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED:
enable.enable = 1;
break;
default:
@@ -4425,7 +4437,7 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst,
dprintk(VIDC_ERR,
"Core %pK in bad state, ignoring prepare buf\n",
inst->core);
- goto exit;
+ return -EINVAL;
}
switch (b->type) {
@@ -4473,7 +4485,7 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst,
"Buffer type not recognized: %d\n", b->type);
break;
}
-exit:
+
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 1ff2ca4cb91f..7caf61cb6799 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -14,13 +14,14 @@
#include <linux/dma-direction.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/bsearch.h>
+#include <linux/delay.h>
#include <media/msm_vidc.h>
#include "msm_vidc_internal.h"
#include "msm_vidc_debug.h"
#include "msm_vdec.h"
#include "msm_venc.h"
#include "msm_vidc_common.h"
-#include <linux/delay.h>
#include "vidc_hfi_api.h"
#include "msm_vidc_dcvs.h"
@@ -88,7 +89,7 @@ int msm_vidc_querycap(void *instance, struct v4l2_capability *cap)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_querycap(instance, cap);
+ return msm_vdec_querycap(inst, cap);
else if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_querycap(instance, cap);
return -EINVAL;
@@ -103,7 +104,7 @@ int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_enum_fmt(instance, f);
+ return msm_vdec_enum_fmt(inst, f);
else if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_enum_fmt(instance, f);
return -EINVAL;
@@ -138,6 +139,101 @@ int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl)
}
EXPORT_SYMBOL(msm_vidc_query_ctrl);
+static int msm_vidc_queryctrl_bsearch_cmp1(const void *key, const void *elt)
+{
+ return *(int32_t *)key - (int32_t)((struct msm_vidc_ctrl *)elt)->id;
+}
+
+static int msm_vidc_queryctrl_bsearch_cmp2(const void *key, const void *elt)
+{
+ uint32_t id = *(uint32_t *)key;
+ struct msm_vidc_ctrl *ctrl = (struct msm_vidc_ctrl *)elt;
+
+ if (id >= ctrl[0].id && id < ctrl[1].id)
+ return 0;
+ else if (id < ctrl[0].id)
+ return -1;
+ else
+ return 1;
+}
+
+int msm_vidc_query_ext_ctrl(void *instance, struct v4l2_query_ext_ctrl *ctrl)
+{
+ struct msm_vidc_inst *inst = instance;
+ bool get_next_ctrl = 0;
+ int i, num_ctrls, rc = 0;
+ struct msm_vidc_ctrl *key = NULL;
+ struct msm_vidc_ctrl *msm_vdec_ctrls;
+
+ if (!inst || !ctrl)
+ return -EINVAL;
+
+ i = ctrl->id;
+ memset(ctrl, 0, sizeof(struct v4l2_query_ext_ctrl));
+ ctrl->id = i;
+
+ if (ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)
+ get_next_ctrl = 1;
+ else if (ctrl->id & V4L2_CTRL_FLAG_NEXT_COMPOUND)
+ goto query_ext_ctrl_err;
+
+ ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
+ ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_COMPOUND;
+
+ if (ctrl->id > V4L2_CID_PRIVATE_BASE ||
+ (ctrl->id >= V4L2_CID_BASE && ctrl->id <= V4L2_CID_LASTP1))
+ goto query_ext_ctrl_err;
+ else if (ctrl->id == V4L2_CID_PRIVATE_BASE && get_next_ctrl)
+ ctrl->id = V4L2_CID_MPEG_MSM_VIDC_BASE;
+
+ if (inst->session_type == MSM_VIDC_DECODER)
+ msm_vdec_g_ctrl(&msm_vdec_ctrls, &num_ctrls);
+ else
+ return -EINVAL;
+
+ if (!get_next_ctrl)
+ key = bsearch(&ctrl->id, msm_vdec_ctrls, num_ctrls,
+ sizeof(struct msm_vidc_ctrl),
+ msm_vidc_queryctrl_bsearch_cmp1);
+ else {
+ key = bsearch(&ctrl->id, msm_vdec_ctrls, num_ctrls-1,
+ sizeof(struct msm_vidc_ctrl),
+ msm_vidc_queryctrl_bsearch_cmp2);
+
+ if (key && ctrl->id > key->id)
+ key++;
+ if (key) {
+ for (i = key-msm_vdec_ctrls, key = NULL;
+ i < num_ctrls; i++)
+ if (!(msm_vdec_ctrls[i].flags &
+ V4L2_CTRL_FLAG_DISABLED)) {
+ key = &msm_vdec_ctrls[i];
+ break;
+ }
+ }
+ }
+
+ if (key) {
+ ctrl->id = key->id;
+ ctrl->type = key->type;
+ strlcpy(ctrl->name, key->name, MAX_NAME_LENGTH);
+ ctrl->minimum = key->minimum;
+ ctrl->maximum = key->maximum;
+ ctrl->step = key->step;
+ ctrl->default_value = key->default_value;
+ ctrl->flags = key->flags;
+ ctrl->elems = 1;
+ ctrl->nr_of_dims = 0;
+ return rc;
+ }
+
+query_ext_ctrl_err:
+ ctrl->name[0] = '\0';
+ ctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_query_ext_ctrl);
+
int msm_vidc_s_fmt(void *instance, struct v4l2_format *f)
{
struct msm_vidc_inst *inst = instance;
@@ -146,7 +242,7 @@ int msm_vidc_s_fmt(void *instance, struct v4l2_format *f)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_s_fmt(instance, f);
+ return msm_vdec_s_fmt(inst, f);
if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_s_fmt(instance, f);
return -EINVAL;
@@ -161,7 +257,7 @@ int msm_vidc_g_fmt(void *instance, struct v4l2_format *f)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_g_fmt(instance, f);
+ return msm_vdec_g_fmt(inst, f);
else if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_g_fmt(instance, f);
return -EINVAL;
@@ -197,7 +293,7 @@ int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_s_ext_ctrl(instance, control);
+ return msm_vdec_s_ext_ctrl(inst, control);
if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_s_ext_ctrl(instance, control);
return -EINVAL;
@@ -212,7 +308,7 @@ int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_reqbufs(instance, b);
+ return msm_vdec_reqbufs(inst, b);
if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_reqbufs(instance, b);
return -EINVAL;
@@ -435,13 +531,60 @@ static inline void save_v4l2_buffer(struct v4l2_buffer *b,
}
}
+static int __map_and_update_binfo(struct msm_vidc_inst *inst,
+ struct buffer_info *binfo,
+ struct v4l2_buffer *b, int i)
+{
+ int rc = 0;
+ struct msm_smem *same_fd_handle = NULL;
+
+ same_fd_handle = get_same_fd_buffer(
+ inst, b->m.planes[i].reserved[0]);
+
+ if (same_fd_handle) {
+ binfo->device_addr[i] =
+ same_fd_handle->device_addr + binfo->buff_off[i];
+ b->m.planes[i].m.userptr = binfo->device_addr[i];
+ binfo->handle[i] = same_fd_handle;
+ } else {
+ binfo->handle[i] = map_buffer(inst, &b->m.planes[i],
+ get_hal_buffer_type(inst, b));
+ if (!binfo->handle[i])
+ rc = -EINVAL;
+
+ binfo->mapped[i] = true;
+ binfo->device_addr[i] = binfo->handle[i]->device_addr +
+ binfo->buff_off[i];
+ b->m.planes[i].m.userptr = binfo->device_addr[i];
+ }
+
+ return rc;
+}
+
+static int __handle_fw_referenced_buffers(struct msm_vidc_inst *inst,
+ struct buffer_info *binfo,
+ struct v4l2_buffer *b)
+{
+ int i = 0, rc = 0;
+
+ if (EXTRADATA_IDX(b->length)) {
+ i = EXTRADATA_IDX(b->length);
+ if (b->m.planes[i].length)
+ rc = __map_and_update_binfo(inst, binfo, b, i);
+ }
+
+ if (rc)
+ dprintk(VIDC_ERR, "%s: Failed to map extradata\n", __func__);
+
+ return rc;
+}
+
int map_and_register_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
{
struct buffer_info *binfo = NULL;
struct buffer_info *temp = NULL, *iterator = NULL;
int plane = 0;
int i = 0, rc = 0;
- struct msm_smem *same_fd_handle = NULL;
if (!b || !inst) {
dprintk(VIDC_ERR, "%s: invalid input\n", __func__);
@@ -517,33 +660,17 @@ int map_and_register_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
rc = 0;
goto exit;
} else if (rc == 2) {
- rc = -EEXIST;
+ rc = __handle_fw_referenced_buffers(inst, temp, b);
+ if (!rc)
+ rc = -EEXIST;
goto exit;
}
- same_fd_handle = get_same_fd_buffer(
- inst, b->m.planes[i].reserved[0]);
-
populate_buf_info(binfo, b, i);
- if (same_fd_handle) {
- binfo->device_addr[i] =
- same_fd_handle->device_addr + binfo->buff_off[i];
- b->m.planes[i].m.userptr = binfo->device_addr[i];
- binfo->mapped[i] = false;
- binfo->handle[i] = same_fd_handle;
- } else {
- binfo->handle[i] = map_buffer(inst, &b->m.planes[i],
- get_hal_buffer_type(inst, b));
- if (!binfo->handle[i]) {
- rc = -EINVAL;
- goto exit;
- }
- binfo->mapped[i] = true;
- binfo->device_addr[i] = binfo->handle[i]->device_addr +
- binfo->buff_off[i];
- b->m.planes[i].m.userptr = binfo->device_addr[i];
- }
+ rc = __map_and_update_binfo(inst, binfo, b, i);
+ if (rc)
+ goto exit;
/* We maintain one ref count for all planes*/
if (!i && is_dynamic_output_buffer_mode(b, inst)) {
@@ -631,6 +758,7 @@ int unmap_and_deregister_buf(struct msm_vidc_inst *inst,
temp->handle[i] = 0;
temp->device_addr[i] = 0;
temp->uvaddr[i] = 0;
+ temp->mapped[i] = false;
}
}
if (!keep_node) {
@@ -670,10 +798,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 +815,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;
}
@@ -737,7 +877,7 @@ int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_prepare_buf(instance, b);
+ return msm_vdec_prepare_buf(inst, b);
if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_prepare_buf(instance, b);
return -EINVAL;
@@ -804,8 +944,7 @@ int msm_vidc_release_buffers(void *instance, int buffer_type)
if (!release_buf)
continue;
if (inst->session_type == MSM_VIDC_DECODER)
- rc = msm_vdec_release_buf(instance,
- &buffer_info);
+ rc = msm_vdec_release_buf(inst, &buffer_info);
if (inst->session_type == MSM_VIDC_ENCODER)
rc = msm_venc_release_buf(instance,
&buffer_info);
@@ -858,6 +997,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 +1045,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 +1055,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);
@@ -926,7 +1071,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
}
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_qbuf(instance, b);
+ return msm_vdec_qbuf(inst, b);
if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_qbuf(instance, b);
@@ -985,7 +1130,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;
@@ -1012,7 +1157,7 @@ int msm_vidc_streamon(void *instance, enum v4l2_buf_type i)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_streamon(instance, i);
+ return msm_vdec_streamon(inst, i);
if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_streamon(instance, i);
return -EINVAL;
@@ -1027,7 +1172,7 @@ int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_streamoff(instance, i);
+ return msm_vdec_streamoff(inst, i);
if (inst->session_type == MSM_VIDC_ENCODER)
return msm_venc_streamoff(instance, i);
return -EINVAL;
@@ -1347,8 +1492,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,
@@ -1362,7 +1505,6 @@ static void cleanup_instance(struct msm_vidc_inst *inst)
int msm_vidc_destroy(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core;
- int i = 0;
if (!inst || !inst->core)
return -EINVAL;
@@ -1386,14 +1528,13 @@ int msm_vidc_destroy(struct msm_vidc_inst *inst)
v4l2_fh_del(&inst->event_handler);
v4l2_fh_exit(&inst->event_handler);
- for (i = 0; i < MAX_PORT_NUM; i++)
- vb2_queue_release(&inst->bufq[i].vb2_bufq);
-
mutex_destroy(&inst->sync_lock);
mutex_destroy(&inst->bufq[CAPTURE_PORT].lock);
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);
@@ -1412,7 +1553,7 @@ int msm_vidc_close(void *instance)
struct msm_vidc_inst *inst = instance;
struct buffer_info *bi, *dummy;
- int rc = 0;
+ int rc = 0, i = 0;
if (!inst || !inst->core)
return -EINVAL;
@@ -1449,6 +1590,12 @@ int msm_vidc_close(void *instance)
msm_comm_session_clean(inst);
msm_smem_delete_client(inst->mem_client);
+ for (i = 0; i < MAX_PORT_NUM; i++) {
+ mutex_lock(&inst->bufq[i].lock);
+ vb2_queue_release(&inst->bufq[i].vb2_bufq);
+ mutex_unlock(&inst->bufq[i].lock);
+ }
+
kref_put(&inst->kref, close_helper);
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 7b28e80979f2..f85fe6fd3867 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -22,6 +22,7 @@
#include "vidc_hfi_api.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_dcvs.h"
+#include "msm_vdec.h"
#define IS_ALREADY_IN_STATE(__p, __d) ({\
int __rc = (__p >= __d);\
@@ -3174,7 +3175,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 +3266,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 +4525,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 +5160,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,
@@ -5426,3 +5434,7 @@ static void msm_comm_print_debug_info(struct msm_vidc_inst *inst)
}
mutex_unlock(&core->lock);
}
+void msm_comm_sort_ctrl(void)
+{
+ msm_vdec_ctrl_sort();
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index eac7f658eb31..7d28859c040c 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);
@@ -99,4 +99,5 @@ void msm_comm_cleanup_internal_buffers(struct msm_vidc_inst *inst);
int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a);
bool msm_comm_turbo_session(struct msm_vidc_inst *inst);
void msm_comm_print_inst_info(struct msm_vidc_inst *inst);
+void msm_comm_sort_ctrl(void);
#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
index 3e269576c126..9bc313adb10a 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
@@ -407,8 +407,10 @@ static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst)
if (dcvs->etb_counter < total_input_buf) {
dcvs->etb_counter++;
- if (dcvs->etb_counter != total_input_buf)
- return rc;
+ if (dcvs->etb_counter != total_input_buf) {
+ return msm_comm_scale_clocks_load(core, dcvs->load,
+ LOAD_CALC_NO_QUIRKS);
+ }
}
dprintk(VIDC_PROF,
@@ -425,7 +427,7 @@ static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst)
}
if (fw_pending_bufs >= DCVS_ENC_HIGH_THR &&
- dcvs->load <= dcvs->load_low) {
+ dcvs->load < dcvs->load_high) {
dcvs->load = dcvs->load_high;
dcvs->prev_freq_increased = true;
} else {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index a9b367d6fe93..5c13b6fef3ec 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -12,6 +12,7 @@
*/
#define CREATE_TRACE_POINTS
+#define MAX_SSR_STRING_LEN 10
#include "msm_vidc_debug.h"
#include "vidc_hfi_api.h"
@@ -37,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,
@@ -83,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;
@@ -90,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;
}
@@ -136,17 +135,33 @@ static int trigger_ssr_open(struct inode *inode, struct file *file)
static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos) {
- u32 ssr_trigger_val;
- int rc;
+ unsigned long ssr_trigger_val = 0;
+ int rc = 0;
struct msm_vidc_core *core = filp->private_data;
- rc = sscanf(buf, "%d", &ssr_trigger_val);
- if (rc < 0) {
+ size_t size = MAX_SSR_STRING_LEN;
+ char kbuf[MAX_SSR_STRING_LEN + 1] = {0};
+
+ if (!count)
+ goto exit;
+
+ if (count < size)
+ size = count;
+
+ if (copy_from_user(kbuf, buf, size)) {
+ dprintk(VIDC_WARN, "%s User memory fault\n", __func__);
+ rc = -EFAULT;
+ goto exit;
+ }
+
+ rc = kstrtoul(kbuf, 0, &ssr_trigger_val);
+ if (rc) {
dprintk(VIDC_WARN, "returning error err %d\n", rc);
rc = -EINVAL;
} else {
msm_vidc_trigger_ssr(core, ssr_trigger_val);
rc = count;
}
+exit:
return rc;
}
@@ -160,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;
@@ -246,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) {
@@ -260,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)
{
@@ -433,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/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 4cc977e568ee..59783dc87e5b 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -1137,8 +1137,11 @@ int read_platform_resources_from_dt(
"qcom,max-secure-instances",
&res->max_secure_inst_count);
- res->cx_ipeak_context = cx_ipeak_register(pdev->dev.of_node,
- "qcom,cx-ipeak-data");
+ if (of_find_property(pdev->dev.of_node,
+ "qcom,cx-ipeak-data", NULL)) {
+ res->cx_ipeak_context = cx_ipeak_register(
+ pdev->dev.of_node, "qcom,cx-ipeak-data");
+ }
if (IS_ERR(res->cx_ipeak_context)) {
rc = PTR_ERR(res->cx_ipeak_context);
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/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 820c8685a75b..6cc5f9f50ba1 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.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
@@ -201,7 +201,7 @@ enum hal_property {
HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL,
HAL_CONFIG_VENC_MAX_BITRATE,
HAL_PARAM_VENC_H264_VUI_TIMING_INFO,
- HAL_PARAM_VENC_H264_GENERATE_AUDNAL,
+ HAL_PARAM_VENC_GENERATE_AUDNAL,
HAL_PARAM_VENC_MAX_NUM_B_FRAMES,
HAL_PARAM_BUFFER_ALLOC_MODE,
HAL_PARAM_VDEC_FRAME_ASSEMBLY,
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index bb9958b0a819..31af06cd88ef 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.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
@@ -340,7 +340,7 @@ struct hfi_buffer_info {
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x014)
#define HFI_PROPERTY_PARAM_VENC_H264_PPS_ID \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x015)
-#define HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL \
+#define HFI_PROPERTY_PARAM_VENC_GENERATE_AUDNAL \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x016)
#define HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x017)
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/pvrusb2/pvrusb2-eeprom.c b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
index e1907cd0c3b7..7613d1fee104 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
@@ -123,15 +123,10 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
memset(&tvdata,0,sizeof(tvdata));
eeprom = pvr2_eeprom_fetch(hdw);
- if (!eeprom) return -EINVAL;
-
- {
- struct i2c_client fake_client;
- /* Newer version expects a useless client interface */
- fake_client.addr = hdw->eeprom_addr;
- fake_client.adapter = &hdw->i2c_adap;
- tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom);
- }
+ if (!eeprom)
+ return -EINVAL;
+
+ tveeprom_hauppauge_analog(NULL, &tvdata, eeprom);
trace_eeprom("eeprom assumed v4l tveeprom module");
trace_eeprom("eeprom direct call results:");
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/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 47f37683893a..3dc9ed2e0774 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -793,7 +793,7 @@ EXPORT_SYMBOL_GPL(vb2_core_create_bufs);
*/
void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
{
- if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
+ if (plane_no >= vb->num_planes || !vb->planes[plane_no].mem_priv)
return NULL;
return call_ptr_memop(vb, vaddr, vb->planes[plane_no].mem_priv);
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index c30290f33430..fe51e9709210 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -375,8 +375,8 @@ int omap_tll_init(struct usbhs_omap_platform_data *pdata)
* and use SDR Mode
*/
reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
- | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
| OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
+ reg |= OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF;
} else if (pdata->port_mode[i] ==
OMAP_EHCI_PORT_MODE_HSIC) {
/*
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2a4abf736d89..d86795bf9453 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -551,7 +551,7 @@ config VEXPRESS_SYSCFG
config UID_SYS_STATS
bool "Per-UID statistics"
- depends on PROFILING
+ depends on PROFILING && TASK_XACCT && TASK_IO_ACCOUNTING
help
Per UID based cpu time statistics exported to /proc/uid_cputime
Per UID based io statistics exported to /proc/uid_io
diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c
index 5484301d57d9..3dc61ea7dc64 100644
--- a/drivers/misc/c2port/c2port-duramar2150.c
+++ b/drivers/misc/c2port/c2port-duramar2150.c
@@ -129,8 +129,8 @@ static int __init duramar2150_c2port_init(void)
duramar2150_c2port_dev = c2port_device_register("uc",
&duramar2150_c2port_ops, NULL);
- if (!duramar2150_c2port_dev) {
- ret = -ENODEV;
+ if (IS_ERR(duramar2150_c2port_dev)) {
+ ret = PTR_ERR(duramar2150_c2port_dev);
goto free_region;
}
diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c
index 783337d22f36..10a02934bfc0 100644
--- a/drivers/misc/cxl/file.c
+++ b/drivers/misc/cxl/file.c
@@ -158,11 +158,8 @@ static long afu_ioctl_start_work(struct cxl_context *ctx,
/* Do this outside the status_mutex to avoid a circular dependency with
* the locking in cxl_mmap_fault() */
- if (copy_from_user(&work, uwork,
- sizeof(struct cxl_ioctl_start_work))) {
- rc = -EFAULT;
- goto out;
- }
+ if (copy_from_user(&work, uwork, sizeof(work)))
+ return -EFAULT;
mutex_lock(&ctx->status_mutex);
if (ctx->status != OPENED) {
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c
index 33ec0c15efa6..c6f2dbfe573d 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp.c
@@ -12,10 +12,13 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/list.h>
@@ -30,6 +33,8 @@
#include <linux/errno.h>
#include <linux/hdcp_qseecom.h>
#include <linux/kthread.h>
+#include <linux/of.h>
+#include <video/msm_hdmi_hdcp_mgr.h>
#include "qseecom_kernel.h"
@@ -542,6 +547,24 @@ struct hdcp_lib_message_map {
const char *msg_name;
};
+struct msm_hdcp_mgr {
+ struct platform_device *pdev;
+ dev_t dev_num;
+ struct cdev cdev;
+ struct class *class;
+ struct device *device;
+ struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
+ u32 tp_msgid;
+ void *client_ctx;
+ struct hdcp_lib_handle *handle;
+};
+
+#define CLASS_NAME "hdcp"
+#define DRIVER_NAME "msm_hdcp"
+
+static struct msm_hdcp_mgr *hdcp_drv_mgr;
+static struct hdcp_lib_handle *drv_client_handle;
+
static void hdcp_lib_clean(struct hdcp_lib_handle *handle);
static void hdcp_lib_init(struct hdcp_lib_handle *handle);
static void hdcp_lib_msg_sent(struct hdcp_lib_handle *handle);
@@ -2288,7 +2311,7 @@ int hdcp1_set_enc(bool enable)
}
if (hdcp1_enc_enabled == enable) {
- pr_debug("already %s\n", enable ? "enabled" : "disabled");
+ pr_info("already %s\n", enable ? "enabled" : "disabled");
goto end;
}
@@ -2318,7 +2341,7 @@ int hdcp1_set_enc(bool enable)
}
hdcp1_enc_enabled = enable;
- pr_debug("%s success\n", enable ? "enable" : "disable");
+ pr_info("%s success\n", enable ? "enable" : "disable");
end:
mutex_unlock(&hdcp1_ta_cmd_lock);
return rc;
@@ -2393,7 +2416,13 @@ int hdcp_library_register(struct hdcp_register_data *data)
}
*data->hdcp_ctx = handle;
+ /* Cache the client ctx to be used later
+ * HDCP driver probe happens earlier than
+ * SDE driver probe hence caching it to
+ * be used later.
+ */
+ drv_client_handle = handle;
handle->thread = kthread_run(kthread_worker_fn,
&handle->worker, "hdcp_tz_lib");
@@ -2433,3 +2462,273 @@ void hdcp_library_deregister(void *phdcpcontext)
kzfree(handle);
}
EXPORT_SYMBOL(hdcp_library_deregister);
+
+void hdcp1_notify_topology(void)
+{
+ char *envp[4];
+ char *a;
+ char *b;
+
+ a = kzalloc(SZ_16, GFP_KERNEL);
+
+ if (!a)
+ return;
+
+ b = kzalloc(SZ_16, GFP_KERNEL);
+
+ if (!b) {
+ kfree(a);
+ return;
+ }
+
+ envp[0] = "HDCP_MGR_EVENT=MSG_READY";
+ envp[1] = a;
+ envp[2] = b;
+ envp[3] = NULL;
+
+ snprintf(envp[1], 16, "%d", (int)DOWN_CHECK_TOPOLOGY);
+ snprintf(envp[2], 16, "%d", (int)HDCP_V1_TX);
+
+ kobject_uevent_env(&hdcp_drv_mgr->device->kobj, KOBJ_CHANGE, envp);
+ kfree(a);
+ kfree(b);
+}
+
+static ssize_t msm_hdcp_1x_sysfs_rda_tp(struct device *dev,
+struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ if (!hdcp_drv_mgr) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ switch (hdcp_drv_mgr->tp_msgid) {
+ case DOWN_CHECK_TOPOLOGY:
+ case DOWN_REQUEST_TOPOLOGY:
+ buf[MSG_ID_IDX] = hdcp_drv_mgr->tp_msgid;
+ buf[RET_CODE_IDX] = HDCP_AUTHED;
+ ret = HEADER_LEN;
+
+ memcpy(buf + HEADER_LEN, &hdcp_drv_mgr->cached_tp,
+ sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+
+ ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
+
+ /* clear the flag once data is read back to user space*/
+ hdcp_drv_mgr->tp_msgid = -1;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+} /* hdcp_1x_sysfs_rda_tp*/
+
+static ssize_t msm_hdcp_1x_sysfs_wta_tp(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int msgid = 0;
+ ssize_t ret = count;
+
+ if (!hdcp_drv_mgr || !buf) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ msgid = buf[0];
+
+ switch (msgid) {
+ case DOWN_CHECK_TOPOLOGY:
+ case DOWN_REQUEST_TOPOLOGY:
+ hdcp_drv_mgr->tp_msgid = msgid;
+ break;
+ /* more cases added here */
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+} /* hdmi_tx_sysfs_wta_hpd */
+
+static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ int min_enc_lvl;
+ struct hdcp_lib_handle *handle;
+ ssize_t ret = count;
+
+ handle = hdcp_drv_mgr->handle;
+
+ rc = kstrtoint(buf, 10, &min_enc_lvl);
+ if (rc) {
+ pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc);
+ return -EINVAL;
+ }
+
+ if (handle && handle->client_ops->notify_lvl_change) {
+ handle->client_ops->notify_lvl_change(handle->client_ctx,
+ min_enc_lvl);
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(tp, S_IRUGO | S_IWUSR, msm_hdcp_1x_sysfs_rda_tp,
+msm_hdcp_1x_sysfs_wta_tp);
+
+static DEVICE_ATTR(min_level_change, S_IWUSR, NULL,
+hdmi_hdcp2p2_sysfs_wta_min_level_change);
+
+void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp)
+{
+ memcpy((void *)&hdcp_drv_mgr->cached_tp,
+ hdcp1_cached_tp,
+ sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+}
+
+static struct attribute *msm_hdcp_fs_attrs[] = {
+ &dev_attr_tp.attr,
+ &dev_attr_min_level_change.attr,
+ NULL
+};
+
+static struct attribute_group msm_hdcp_fs_attr_group = {
+ .attrs = msm_hdcp_fs_attrs
+};
+
+static int msm_hdcp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int msm_hdcp_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations msm_hdcp_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_hdcp_open,
+ .release = msm_hdcp_close,
+};
+
+static const struct of_device_id msm_hdcp_dt_match[] = {
+ { .compatible = "qcom,msm-hdcp",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
+
+static int msm_hdcp_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ hdcp_drv_mgr = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp_mgr),
+ GFP_KERNEL);
+ if (!hdcp_drv_mgr)
+ return -ENOMEM;
+
+ hdcp_drv_mgr->pdev = pdev;
+
+ platform_set_drvdata(pdev, hdcp_drv_mgr);
+
+ ret = alloc_chrdev_region(&hdcp_drv_mgr->dev_num, 0, 1, DRIVER_NAME);
+ if (ret < 0) {
+ pr_err("alloc_chrdev_region failed ret = %d\n", ret);
+ goto error_get_dev_num;
+ }
+
+ hdcp_drv_mgr->class = class_create(THIS_MODULE, CLASS_NAME);
+ if (IS_ERR(hdcp_drv_mgr->class)) {
+ ret = PTR_ERR(hdcp_drv_mgr->class);
+ pr_err("couldn't create class rc = %d\n", ret);
+ goto error_class_create;
+ }
+
+ hdcp_drv_mgr->device = device_create(hdcp_drv_mgr->class, NULL,
+ hdcp_drv_mgr->dev_num, NULL, DRIVER_NAME);
+ if (IS_ERR(hdcp_drv_mgr->device)) {
+ ret = PTR_ERR(hdcp_drv_mgr->device);
+ pr_err("device_create failed %d\n", ret);
+ goto error_class_device_create;
+ }
+
+ cdev_init(&hdcp_drv_mgr->cdev, &msm_hdcp_fops);
+ ret = cdev_add(&hdcp_drv_mgr->cdev,
+ MKDEV(MAJOR(hdcp_drv_mgr->dev_num), 0), 1);
+ if (ret < 0) {
+ pr_err("cdev_add failed %d\n", ret);
+ goto error_cdev_add;
+ }
+
+ ret = sysfs_create_group(&hdcp_drv_mgr->device->kobj,
+ &msm_hdcp_fs_attr_group);
+ if (ret)
+ pr_err("unable to register rotator sysfs nodes\n");
+
+ /* Store the handle in the hdcp drv mgr
+ * to be used for the sysfs notifications
+ */
+ hdcp_drv_mgr->handle = drv_client_handle;
+
+ return 0;
+error_cdev_add:
+ device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num);
+error_class_device_create:
+ class_destroy(hdcp_drv_mgr->class);
+error_class_create:
+ unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1);
+error_get_dev_num:
+ devm_kfree(&pdev->dev, hdcp_drv_mgr);
+ hdcp_drv_mgr = NULL;
+ return ret;
+}
+
+static int msm_hdcp_remove(struct platform_device *pdev)
+{
+ struct msm_hdcp_mgr *mgr;
+
+ mgr = (struct msm_hdcp_mgr *)platform_get_drvdata(pdev);
+ if (!mgr)
+ return -ENODEV;
+
+ sysfs_remove_group(&hdcp_drv_mgr->device->kobj,
+ &msm_hdcp_fs_attr_group);
+ cdev_del(&hdcp_drv_mgr->cdev);
+ device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num);
+ class_destroy(hdcp_drv_mgr->class);
+ unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1);
+
+ devm_kfree(&pdev->dev, hdcp_drv_mgr);
+ hdcp_drv_mgr = NULL;
+ return 0;
+}
+
+static struct platform_driver msm_hdcp_driver = {
+ .probe = msm_hdcp_probe,
+ .remove = msm_hdcp_remove,
+ .driver = {
+ .name = "msm_hdcp",
+ .of_match_table = msm_hdcp_dt_match,
+ .pm = NULL,
+ }
+};
+
+static int __init msm_hdcp_init(void)
+{
+ return platform_driver_register(&msm_hdcp_driver);
+}
+
+static void __exit msm_hdcp_exit(void)
+{
+ return platform_driver_unregister(&msm_hdcp_driver);
+}
+
+module_init(msm_hdcp_init);
+module_exit(msm_hdcp_exit);
+
+MODULE_DESCRIPTION("MSM HDCP driver");
+MODULE_LICENSE("GPL v2");
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 1c7cc917faa6..7cdcd69cecf4 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1257,7 +1257,7 @@ static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
atomic_read(&data->ioctl_count) <= 1)) {
pr_err("Interrupted from abort\n");
ret = -ERESTARTSYS;
- break;
+ return ret;
}
}
@@ -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;
@@ -4375,9 +4379,9 @@ int qseecom_start_app(struct qseecom_handle **handle,
return -EINVAL;
}
- if (strlen(app_name) >= MAX_APP_NAME_SIZE) {
+ if (strnlen(app_name, MAX_APP_NAME_SIZE) == MAX_APP_NAME_SIZE) {
pr_err("The app_name (%s) with length %zu is not valid\n",
- app_name, strlen(app_name));
+ app_name, strnlen(app_name, MAX_APP_NAME_SIZE));
return -EINVAL;
}
@@ -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..3c9d311106cd 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;
@@ -93,7 +94,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid)
static int uid_cputime_show(struct seq_file *m, void *v)
{
- struct uid_entry *uid_entry;
+ struct uid_entry *uid_entry = NULL;
struct task_struct *task, *temp;
struct user_namespace *user_ns = current_user_ns();
cputime_t utime;
@@ -111,7 +112,8 @@ static int uid_cputime_show(struct seq_file *m, void *v)
read_lock(&tasklist_lock);
do_each_thread(temp, task) {
uid = from_kuid_munged(user_ns, task_uid(task));
- uid_entry = find_or_register_uid(uid);
+ if (!uid_entry || uid_entry->uid != uid)
+ uid_entry = find_or_register_uid(uid);
if (!uid_entry) {
read_unlock(&tasklist_lock);
rt_mutex_unlock(&uid_lock);
@@ -214,35 +216,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 uid_entry *uid_entry = NULL;
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;
@@ -254,73 +265,42 @@ static void update_io_stats_all_locked(void)
rcu_read_lock();
do_each_thread(temp, task) {
uid = from_kuid_munged(user_ns, task_uid(task));
- uid_entry = find_or_register_uid(uid);
+ if (!uid_entry || uid_entry->uid != uid)
+ 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 +385,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 +423,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/card/block.c b/drivers/mmc/card/block.c
index 69e51cc96303..29c57d13744e 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1017,7 +1017,7 @@ static int mmc_blk_ioctl_rpmb_cmd(struct block_device *bdev,
{
struct mmc_blk_ioc_rpmb_data *idata;
struct mmc_blk_data *md;
- struct mmc_card *card;
+ struct mmc_card *card = NULL;
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct mmc_request mrq = {NULL};
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 63f7bf87843f..79b5b3504ccd 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;
@@ -1709,7 +1735,6 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
struct mmc_async_req *areq, int *error)
{
int err = 0;
- int start_err = 0;
struct mmc_async_req *data = host->areq;
/* Prepare a new request */
@@ -1758,7 +1783,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
trace_mmc_blk_rw_start(areq->mrq->cmd->opcode,
areq->mrq->cmd->arg,
areq->mrq->data);
- start_err = __mmc_start_data_req(host, areq->mrq);
+ __mmc_start_data_req(host, areq->mrq);
}
if (host->areq)
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 594fba08e623..72bfdd835178 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -337,10 +337,15 @@ static int mmc_force_err_set(void *data, u64 val)
{
struct mmc_host *host = data;
- if (host && host->ops && host->ops->force_err_irq) {
- mmc_host_clk_hold(host);
+ if (host && host->card && host->ops &&
+ host->ops->force_err_irq) {
+ /*
+ * To access the force error irq reg, we need to make
+ * sure the host is powered up and host clock is ticking.
+ */
+ mmc_get_card(host->card);
host->ops->force_err_irq(host, val);
- mmc_host_clk_release(host);
+ mmc_put_card(host->card);
}
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-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 3b423b0ad8e7..f280744578e4 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -156,7 +156,8 @@ static const struct sdhci_ops sdhci_iproc_ops = {
};
static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = {
- .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
+ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
.ops = &sdhci_iproc_ops,
};
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index ca72ebfd55a3..d908c3fed7c9 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");
@@ -2366,21 +2366,6 @@ out:
return ret;
}
-/*
- * Reset vreg by ensuring it is off during probe. A call
- * to enable vreg is needed to balance disable vreg
- */
-static int sdhci_msm_vreg_reset(struct sdhci_msm_pltfm_data *pdata)
-{
- int ret;
-
- ret = sdhci_msm_setup_vreg(pdata, 1, true);
- if (ret)
- return ret;
- ret = sdhci_msm_setup_vreg(pdata, 0, true);
- return ret;
-}
-
/* This init function should be called only once for each SDHC slot */
static int sdhci_msm_vreg_init(struct device *dev,
struct sdhci_msm_pltfm_data *pdata,
@@ -2415,7 +2400,7 @@ static int sdhci_msm_vreg_init(struct device *dev,
if (ret)
goto vdd_reg_deinit;
}
- ret = sdhci_msm_vreg_reset(pdata);
+
if (ret)
dev_err(dev, "vreg reset failed (%d)\n", ret);
goto out;
@@ -2592,7 +2577,9 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
io_level = REQ_IO_HIGH;
}
if (irq_status & CORE_PWRCTL_BUS_OFF) {
- ret = sdhci_msm_setup_vreg(msm_host->pdata, false, false);
+ if (msm_host->pltfm_init_done)
+ ret = sdhci_msm_setup_vreg(msm_host->pdata,
+ false, false);
if (!ret) {
ret = sdhci_msm_setup_pins(msm_host->pdata, false);
ret |= sdhci_msm_set_vdd_io_vol(msm_host->pdata,
@@ -2639,7 +2626,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 +4122,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 +
@@ -4666,6 +4655,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto vreg_deinit;
}
+ msm_host->pltfm_init_done = true;
+
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_AUTOSUSPEND_DELAY_MS);
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 92f61708001e..79949c2c537f 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,8 @@ struct sdhci_msm_host {
bool tuning_in_progress;
bool mci_removed;
const struct sdhci_msm_offset *offset;
+ bool core_3_0v_support;
+ bool pltfm_init_done;
};
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/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index cbc99d5649af..ae5709354546 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -246,6 +246,8 @@ static int gs_cmd_reset(struct gs_usb *gsusb, struct gs_can *gsdev)
sizeof(*dm),
1000);
+ kfree(dm);
+
return rc;
}
diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c
index ac7288240d55..f089fa954f42 100644
--- a/drivers/net/ethernet/adaptec/starfire.c
+++ b/drivers/net/ethernet/adaptec/starfire.c
@@ -1153,6 +1153,12 @@ static void init_ring(struct net_device *dev)
if (skb == NULL)
break;
np->rx_info[i].mapping = pci_map_single(np->pci_dev, skb->data, np->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(np->pci_dev,
+ np->rx_info[i].mapping)) {
+ dev_kfree_skb(skb);
+ np->rx_info[i].skb = NULL;
+ break;
+ }
/* Grrr, we cannot offset to correctly align the IP header. */
np->rx_ring[i].rxaddr = cpu_to_dma(np->rx_info[i].mapping | RxDescValid);
}
@@ -1183,8 +1189,9 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
unsigned int entry;
+ unsigned int prev_tx;
u32 status;
- int i;
+ int i, j;
/*
* be cautious here, wrapping the queue has weird semantics
@@ -1202,6 +1209,7 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
}
#endif /* ZEROCOPY && HAS_BROKEN_FIRMWARE */
+ prev_tx = np->cur_tx;
entry = np->cur_tx % TX_RING_SIZE;
for (i = 0; i < skb_num_frags(skb); i++) {
int wrap_ring = 0;
@@ -1235,6 +1243,11 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
skb_frag_size(this_frag),
PCI_DMA_TODEVICE);
}
+ if (pci_dma_mapping_error(np->pci_dev,
+ np->tx_info[entry].mapping)) {
+ dev->stats.tx_dropped++;
+ goto err_out;
+ }
np->tx_ring[entry].addr = cpu_to_dma(np->tx_info[entry].mapping);
np->tx_ring[entry].status = cpu_to_le32(status);
@@ -1269,8 +1282,30 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
netif_stop_queue(dev);
return NETDEV_TX_OK;
-}
+err_out:
+ entry = prev_tx % TX_RING_SIZE;
+ np->tx_info[entry].skb = NULL;
+ if (i > 0) {
+ pci_unmap_single(np->pci_dev,
+ np->tx_info[entry].mapping,
+ skb_first_frag_len(skb),
+ PCI_DMA_TODEVICE);
+ np->tx_info[entry].mapping = 0;
+ entry = (entry + np->tx_info[entry].used_slots) % TX_RING_SIZE;
+ for (j = 1; j < i; j++) {
+ pci_unmap_single(np->pci_dev,
+ np->tx_info[entry].mapping,
+ skb_frag_size(
+ &skb_shinfo(skb)->frags[j-1]),
+ PCI_DMA_TODEVICE);
+ entry++;
+ }
+ }
+ dev_kfree_skb_any(skb);
+ np->cur_tx = prev_tx;
+ return NETDEV_TX_OK;
+}
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
@@ -1570,6 +1605,12 @@ static void refill_rx_ring(struct net_device *dev)
break; /* Better luck next round. */
np->rx_info[entry].mapping =
pci_map_single(np->pci_dev, skb->data, np->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(np->pci_dev,
+ np->rx_info[entry].mapping)) {
+ dev_kfree_skb(skb);
+ np->rx_info[entry].skb = NULL;
+ break;
+ }
np->rx_ring[entry].rxaddr =
cpu_to_dma(np->rx_info[entry].mapping | RxDescValid);
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index c82ab87fcbe8..e5911ccb2148 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -1949,7 +1949,7 @@ u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb,
}
/* select a non-FCoE queue */
- return fallback(dev, skb) % BNX2X_NUM_ETH_QUEUES(bp);
+ return fallback(dev, skb) % (BNX2X_NUM_ETH_QUEUES(bp) * bp->max_cos);
}
void bnx2x_set_num_queues(struct bnx2x *bp)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 0d147610a06f..090e00650601 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -2714,10 +2714,14 @@ static int cxgb_up(struct adapter *adap)
if (err)
goto irq_err;
}
+
+ mutex_lock(&uld_mutex);
enable_rx(adap);
t4_sge_start(adap);
t4_intr_enable(adap);
adap->flags |= FULL_INIT_DONE;
+ mutex_unlock(&uld_mutex);
+
notify_ulds(adap, CXGB4_STATE_UP);
#if IS_ENABLED(CONFIG_IPV6)
update_clip(adap);
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 8a1d9fffd7d6..26255862d1cf 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -5260,9 +5260,11 @@ static netdev_features_t be_features_check(struct sk_buff *skb,
struct be_adapter *adapter = netdev_priv(dev);
u8 l4_hdr = 0;
- /* The code below restricts offload features for some tunneled packets.
+ /* The code below restricts offload features for some tunneled and
+ * Q-in-Q packets.
* Offload features for normal (non tunnel) packets are unchanged.
*/
+ features = vlan_features_check(skb, features);
if (!skb->encapsulation ||
!(adapter->flags & BE_FLAGS_VXLAN_OFFLOADS))
return features;
diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c
index ff665493ca97..52f2230062e7 100644
--- a/drivers/net/ethernet/ethoc.c
+++ b/drivers/net/ethernet/ethoc.c
@@ -713,6 +713,8 @@ static int ethoc_open(struct net_device *dev)
if (ret)
return ret;
+ napi_enable(&priv->napi);
+
ethoc_init_ring(priv, dev->mem_start);
ethoc_reset(priv);
@@ -725,7 +727,6 @@ static int ethoc_open(struct net_device *dev)
}
phy_start(priv->phy);
- napi_enable(&priv->napi);
if (netif_msg_ifup(priv)) {
dev_info(&dev->dev, "I/O: %08lx Memory: %08lx-%08lx\n",
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 3e233d924cce..6a061f17a44f 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -1999,8 +1999,8 @@ static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue)
if (!rxb->page)
continue;
- dma_unmap_single(rx_queue->dev, rxb->dma,
- PAGE_SIZE, DMA_FROM_DEVICE);
+ dma_unmap_page(rx_queue->dev, rxb->dma,
+ PAGE_SIZE, DMA_FROM_DEVICE);
__free_page(rxb->page);
rxb->page = NULL;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
index 08cef0dfb5db..2fa54b0b0679 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
@@ -105,8 +105,8 @@ int hns_nic_net_xmit_hw(struct net_device *ndev,
struct hns_nic_ring_data *ring_data)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
- struct device *dev = priv->dev;
struct hnae_ring *ring = ring_data->ring;
+ struct device *dev = ring_to_dev(ring);
struct netdev_queue *dev_queue;
struct skb_frag_struct *frag;
int buf_num;
diff --git a/drivers/net/ethernet/mellanox/mlx4/catas.c b/drivers/net/ethernet/mellanox/mlx4/catas.c
index 715de8affcc9..e203d0c4e5a3 100644
--- a/drivers/net/ethernet/mellanox/mlx4/catas.c
+++ b/drivers/net/ethernet/mellanox/mlx4/catas.c
@@ -158,7 +158,7 @@ static int mlx4_reset_slave(struct mlx4_dev *dev)
return -ETIMEDOUT;
}
-static int mlx4_comm_internal_err(u32 slave_read)
+int mlx4_comm_internal_err(u32 slave_read)
{
return (u32)COMM_CHAN_EVENT_INTERNAL_ERR ==
(slave_read & (u32)COMM_CHAN_EVENT_INTERNAL_ERR) ? 1 : 0;
diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c
index 0472941af820..1a134e08f010 100644
--- a/drivers/net/ethernet/mellanox/mlx4/intf.c
+++ b/drivers/net/ethernet/mellanox/mlx4/intf.c
@@ -218,6 +218,18 @@ void mlx4_unregister_device(struct mlx4_dev *dev)
struct mlx4_interface *intf;
mlx4_stop_catas_poll(dev);
+ if (dev->persist->interface_state & MLX4_INTERFACE_STATE_DELETION &&
+ mlx4_is_slave(dev)) {
+ /* In mlx4_remove_one on a VF */
+ u32 slave_read =
+ swab32(readl(&mlx4_priv(dev)->mfunc.comm->slave_read));
+
+ if (mlx4_comm_internal_err(slave_read)) {
+ mlx4_dbg(dev, "%s: comm channel is down, entering error state.\n",
+ __func__);
+ mlx4_enter_error_state(dev->persist);
+ }
+ }
mutex_lock(&intf_mutex);
list_for_each_entry(intf, &intf_list, list)
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index e1cf9036af22..f5fdbd53d052 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -1205,6 +1205,7 @@ void mlx4_qp_event(struct mlx4_dev *dev, u32 qpn, int event_type);
void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type);
void mlx4_enter_error_state(struct mlx4_dev_persistent *persist);
+int mlx4_comm_internal_err(u32 slave_read);
int mlx4_SENSE_PORT(struct mlx4_dev *dev, int port,
enum mlx4_port_type *type);
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 549ad2018e7f..1e61d4da72db 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -171,6 +171,49 @@ static struct mdiobb_ops bb_ops = {
.get_mdio_data = ravb_get_mdio_data,
};
+/* Free TX skb function for AVB-IP */
+static int ravb_tx_free(struct net_device *ndev, int q, bool free_txed_only)
+{
+ struct ravb_private *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &priv->stats[q];
+ struct ravb_tx_desc *desc;
+ int free_num = 0;
+ int entry;
+ u32 size;
+
+ for (; priv->cur_tx[q] - priv->dirty_tx[q] > 0; priv->dirty_tx[q]++) {
+ bool txed;
+
+ entry = priv->dirty_tx[q] % (priv->num_tx_ring[q] *
+ NUM_TX_DESC);
+ desc = &priv->tx_ring[q][entry];
+ txed = desc->die_dt == DT_FEMPTY;
+ if (free_txed_only && !txed)
+ break;
+ /* Descriptor type must be checked before all other reads */
+ dma_rmb();
+ size = le16_to_cpu(desc->ds_tagl) & TX_DS;
+ /* Free the original skb. */
+ if (priv->tx_skb[q][entry / NUM_TX_DESC]) {
+ dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
+ size, DMA_TO_DEVICE);
+ /* Last packet descriptor? */
+ if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) {
+ entry /= NUM_TX_DESC;
+ dev_kfree_skb_any(priv->tx_skb[q][entry]);
+ priv->tx_skb[q][entry] = NULL;
+ if (txed)
+ stats->tx_packets++;
+ }
+ free_num++;
+ }
+ if (txed)
+ stats->tx_bytes += size;
+ desc->die_dt = DT_EEMPTY;
+ }
+ return free_num;
+}
+
/* Free skb's and DMA buffers for Ethernet AVB */
static void ravb_ring_free(struct net_device *ndev, int q)
{
@@ -186,19 +229,21 @@ static void ravb_ring_free(struct net_device *ndev, int q)
kfree(priv->rx_skb[q]);
priv->rx_skb[q] = NULL;
- /* Free TX skb ringbuffer */
- if (priv->tx_skb[q]) {
- for (i = 0; i < priv->num_tx_ring[q]; i++)
- dev_kfree_skb(priv->tx_skb[q][i]);
- }
- kfree(priv->tx_skb[q]);
- priv->tx_skb[q] = NULL;
-
/* Free aligned TX buffers */
kfree(priv->tx_align[q]);
priv->tx_align[q] = NULL;
if (priv->rx_ring[q]) {
+ for (i = 0; i < priv->num_rx_ring[q]; i++) {
+ struct ravb_ex_rx_desc *desc = &priv->rx_ring[q][i];
+
+ if (!dma_mapping_error(ndev->dev.parent,
+ le32_to_cpu(desc->dptr)))
+ dma_unmap_single(ndev->dev.parent,
+ le32_to_cpu(desc->dptr),
+ PKT_BUF_SZ,
+ DMA_FROM_DEVICE);
+ }
ring_size = sizeof(struct ravb_ex_rx_desc) *
(priv->num_rx_ring[q] + 1);
dma_free_coherent(ndev->dev.parent, ring_size, priv->rx_ring[q],
@@ -207,12 +252,20 @@ static void ravb_ring_free(struct net_device *ndev, int q)
}
if (priv->tx_ring[q]) {
+ ravb_tx_free(ndev, q, false);
+
ring_size = sizeof(struct ravb_tx_desc) *
(priv->num_tx_ring[q] * NUM_TX_DESC + 1);
dma_free_coherent(ndev->dev.parent, ring_size, priv->tx_ring[q],
priv->tx_desc_dma[q]);
priv->tx_ring[q] = NULL;
}
+
+ /* Free TX skb ringbuffer.
+ * SKBs are freed by ravb_tx_free() call above.
+ */
+ kfree(priv->tx_skb[q]);
+ priv->tx_skb[q] = NULL;
}
/* Format skb and descriptor buffer for Ethernet AVB */
@@ -420,44 +473,6 @@ static int ravb_dmac_init(struct net_device *ndev)
return 0;
}
-/* Free TX skb function for AVB-IP */
-static int ravb_tx_free(struct net_device *ndev, int q)
-{
- struct ravb_private *priv = netdev_priv(ndev);
- struct net_device_stats *stats = &priv->stats[q];
- struct ravb_tx_desc *desc;
- int free_num = 0;
- int entry;
- u32 size;
-
- for (; priv->cur_tx[q] - priv->dirty_tx[q] > 0; priv->dirty_tx[q]++) {
- entry = priv->dirty_tx[q] % (priv->num_tx_ring[q] *
- NUM_TX_DESC);
- desc = &priv->tx_ring[q][entry];
- if (desc->die_dt != DT_FEMPTY)
- break;
- /* Descriptor type must be checked before all other reads */
- dma_rmb();
- size = le16_to_cpu(desc->ds_tagl) & TX_DS;
- /* Free the original skb. */
- if (priv->tx_skb[q][entry / NUM_TX_DESC]) {
- dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
- size, DMA_TO_DEVICE);
- /* Last packet descriptor? */
- if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) {
- entry /= NUM_TX_DESC;
- dev_kfree_skb_any(priv->tx_skb[q][entry]);
- priv->tx_skb[q][entry] = NULL;
- stats->tx_packets++;
- }
- free_num++;
- }
- stats->tx_bytes += size;
- desc->die_dt = DT_EEMPTY;
- }
- return free_num;
-}
-
static void ravb_get_tx_tstamp(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
@@ -797,7 +812,7 @@ static int ravb_poll(struct napi_struct *napi, int budget)
spin_lock_irqsave(&priv->lock, flags);
/* Clear TX interrupt */
ravb_write(ndev, ~mask, TIS);
- ravb_tx_free(ndev, q);
+ ravb_tx_free(ndev, q, true);
netif_wake_subqueue(ndev, q);
mmiowb();
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1393,7 +1408,8 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
priv->cur_tx[q] += NUM_TX_DESC;
if (priv->cur_tx[q] - priv->dirty_tx[q] >
- (priv->num_tx_ring[q] - 1) * NUM_TX_DESC && !ravb_tx_free(ndev, q))
+ (priv->num_tx_ring[q] - 1) * NUM_TX_DESC &&
+ !ravb_tx_free(ndev, q, true))
netif_stop_subqueue(ndev, q);
exit:
diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index cf468c87ce57..4cb8b85cbf2c 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -100,6 +100,14 @@
/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */
#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT)
+#ifdef __BIG_ENDIAN
+#define xemaclite_readl ioread32be
+#define xemaclite_writel iowrite32be
+#else
+#define xemaclite_readl ioread32
+#define xemaclite_writel iowrite32
+#endif
+
/**
* struct net_local - Our private per device data
* @ndev: instance of the network device
@@ -158,15 +166,15 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata)
u32 reg_data;
/* Enable the Tx interrupts for the first Buffer */
- reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
- __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
- drvdata->base_addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET);
+ xemaclite_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
+ drvdata->base_addr + XEL_TSR_OFFSET);
/* Enable the Rx interrupts for the first buffer */
- __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
+ xemaclite_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
/* Enable the Global Interrupt Enable */
- __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
+ xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
}
/**
@@ -181,17 +189,17 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata)
u32 reg_data;
/* Disable the Global Interrupt Enable */
- __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
+ xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
/* Disable the Tx interrupts for the first buffer */
- reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
- __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
- drvdata->base_addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET);
+ xemaclite_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
+ drvdata->base_addr + XEL_TSR_OFFSET);
/* Disable the Rx interrupts for the first buffer */
- reg_data = __raw_readl(drvdata->base_addr + XEL_RSR_OFFSET);
- __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
- drvdata->base_addr + XEL_RSR_OFFSET);
+ reg_data = xemaclite_readl(drvdata->base_addr + XEL_RSR_OFFSET);
+ xemaclite_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
+ drvdata->base_addr + XEL_RSR_OFFSET);
}
/**
@@ -323,7 +331,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
byte_count = ETH_FRAME_LEN;
/* Check if the expected buffer is available */
- reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
XEL_TSR_XMIT_ACTIVE_MASK)) == 0) {
@@ -336,7 +344,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
addr = (void __iomem __force *)((u32 __force)addr ^
XEL_BUFFER_OFFSET);
- reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
XEL_TSR_XMIT_ACTIVE_MASK)) != 0)
@@ -347,16 +355,16 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
/* Write the frame to the buffer */
xemaclite_aligned_write(data, (u32 __force *) addr, byte_count);
- __raw_writel((byte_count & XEL_TPLR_LENGTH_MASK),
- addr + XEL_TPLR_OFFSET);
+ xemaclite_writel((byte_count & XEL_TPLR_LENGTH_MASK),
+ addr + XEL_TPLR_OFFSET);
/* Update the Tx Status Register to indicate that there is a
* frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which
* is used by the interrupt handler to check whether a frame
* has been transmitted */
- reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK);
- __raw_writel(reg_data, addr + XEL_TSR_OFFSET);
+ xemaclite_writel(reg_data, addr + XEL_TSR_OFFSET);
return 0;
}
@@ -371,7 +379,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
*
* Return: Total number of bytes received
*/
-static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
+static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data, int maxlen)
{
void __iomem *addr;
u16 length, proto_type;
@@ -381,7 +389,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);
/* Verify which buffer has valid data */
- reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
if (drvdata->rx_ping_pong != 0)
@@ -398,27 +406,28 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
return 0; /* No data was available */
/* Verify that buffer has valid data */
- reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
XEL_RSR_RECV_DONE_MASK)
return 0; /* No data was available */
}
/* Get the protocol type of the ethernet frame that arrived */
- proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET +
+ proto_type = ((ntohl(xemaclite_readl(addr + XEL_HEADER_OFFSET +
XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) &
XEL_RPLR_LENGTH_MASK);
/* Check if received ethernet frame is a raw ethernet frame
* or an IP packet or an ARP packet */
- if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {
+ if (proto_type > ETH_DATA_LEN) {
if (proto_type == ETH_P_IP) {
- length = ((ntohl(__raw_readl(addr +
+ length = ((ntohl(xemaclite_readl(addr +
XEL_HEADER_IP_LENGTH_OFFSET +
XEL_RXBUFF_OFFSET)) >>
XEL_HEADER_SHIFT) &
XEL_RPLR_LENGTH_MASK);
+ length = min_t(u16, length, ETH_DATA_LEN);
length += ETH_HLEN + ETH_FCS_LEN;
} else if (proto_type == ETH_P_ARP)
@@ -431,14 +440,17 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
/* Use the length in the frame, plus the header and trailer */
length = proto_type + ETH_HLEN + ETH_FCS_LEN;
+ if (WARN_ON(length > maxlen))
+ length = maxlen;
+
/* Read from the EmacLite device */
xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET),
data, length);
/* Acknowledge the frame */
- reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
reg_data &= ~XEL_RSR_RECV_DONE_MASK;
- __raw_writel(reg_data, addr + XEL_RSR_OFFSET);
+ xemaclite_writel(reg_data, addr + XEL_RSR_OFFSET);
return length;
}
@@ -465,14 +477,14 @@ static void xemaclite_update_address(struct net_local *drvdata,
xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN);
- __raw_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET);
+ xemaclite_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET);
/* Update the MAC address in the EmacLite */
- reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
- __raw_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
+ xemaclite_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET);
/* Wait for EmacLite to finish with the MAC address update */
- while ((__raw_readl(addr + XEL_TSR_OFFSET) &
+ while ((xemaclite_readl(addr + XEL_TSR_OFFSET) &
XEL_TSR_PROG_MAC_ADDR) != 0)
;
}
@@ -605,7 +617,7 @@ static void xemaclite_rx_handler(struct net_device *dev)
skb_reserve(skb, 2);
- len = xemaclite_recv_data(lp, (u8 *) skb->data);
+ len = xemaclite_recv_data(lp, (u8 *) skb->data, len);
if (!len) {
dev->stats.rx_errors++;
@@ -642,32 +654,32 @@ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id)
u32 tx_status;
/* Check if there is Rx Data available */
- if ((__raw_readl(base_addr + XEL_RSR_OFFSET) &
+ if ((xemaclite_readl(base_addr + XEL_RSR_OFFSET) &
XEL_RSR_RECV_DONE_MASK) ||
- (__raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
+ (xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
& XEL_RSR_RECV_DONE_MASK))
xemaclite_rx_handler(dev);
/* Check if the Transmission for the first buffer is completed */
- tx_status = __raw_readl(base_addr + XEL_TSR_OFFSET);
+ tx_status = xemaclite_readl(base_addr + XEL_TSR_OFFSET);
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
- __raw_writel(tx_status, base_addr + XEL_TSR_OFFSET);
+ xemaclite_writel(tx_status, base_addr + XEL_TSR_OFFSET);
tx_complete = true;
}
/* Check if the Transmission for the second buffer is completed */
- tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+ tx_status = xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
- __raw_writel(tx_status, base_addr + XEL_BUFFER_OFFSET +
- XEL_TSR_OFFSET);
+ xemaclite_writel(tx_status, base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET);
tx_complete = true;
}
@@ -700,7 +712,7 @@ static int xemaclite_mdio_wait(struct net_local *lp)
/* wait for the MDIO interface to not be busy or timeout
after some time.
*/
- while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
+ while (xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
XEL_MDIOCTRL_MDIOSTS_MASK) {
if (time_before_eq(end, jiffies)) {
WARN_ON(1);
@@ -736,17 +748,17 @@ static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg)
* MDIO Address register. Set the Status bit in the MDIO Control
* register to start a MDIO read transaction.
*/
- ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
- __raw_writel(XEL_MDIOADDR_OP_MASK |
- ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
- lp->base_addr + XEL_MDIOADDR_OFFSET);
- __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
- lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ xemaclite_writel(XEL_MDIOADDR_OP_MASK |
+ ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
+ lp->base_addr + XEL_MDIOADDR_OFFSET);
+ xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
if (xemaclite_mdio_wait(lp))
return -ETIMEDOUT;
- rc = __raw_readl(lp->base_addr + XEL_MDIORD_OFFSET);
+ rc = xemaclite_readl(lp->base_addr + XEL_MDIORD_OFFSET);
dev_dbg(&lp->ndev->dev,
"xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n",
@@ -783,13 +795,13 @@ static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg,
* Data register. Finally, set the Status bit in the MDIO Control
* register to start a MDIO write transaction.
*/
- ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
- __raw_writel(~XEL_MDIOADDR_OP_MASK &
- ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
- lp->base_addr + XEL_MDIOADDR_OFFSET);
- __raw_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET);
- __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
- lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ xemaclite_writel(~XEL_MDIOADDR_OP_MASK &
+ ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
+ lp->base_addr + XEL_MDIOADDR_OFFSET);
+ xemaclite_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET);
+ xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
return 0;
}
@@ -836,8 +848,8 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
/* Enable the MDIO bus by asserting the enable bit in MDIO Control
* register.
*/
- __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK,
- lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ xemaclite_writel(XEL_MDIOCTRL_MDIOEN_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
bus = mdiobus_alloc();
if (!bus) {
@@ -1141,8 +1153,8 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
dev_warn(dev, "No MAC address found\n");
/* Clear the Tx CSR's in case this is a restart */
- __raw_writel(0, lp->base_addr + XEL_TSR_OFFSET);
- __raw_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+ xemaclite_writel(0, lp->base_addr + XEL_TSR_OFFSET);
+ xemaclite_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
/* Set the MAC address in the EmacLite device */
xemaclite_update_address(lp, ndev->dev_addr);
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
index 85828f153445..0758d0816840 100644
--- a/drivers/net/hamradio/mkiss.c
+++ b/drivers/net/hamradio/mkiss.c
@@ -648,8 +648,8 @@ static void ax_setup(struct net_device *dev)
{
/* Finish setting up the DEVICE info. */
dev->mtu = AX_MTU;
- dev->hard_header_len = 0;
- dev->addr_len = 0;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN;
+ dev->addr_len = AX25_ADDR_LEN;
dev->type = ARPHRD_AX25;
dev->tx_queue_len = 10;
dev->header_ops = &ax25_header_ops;
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/phy/marvell.c b/drivers/net/phy/marvell.c
index 0240552b50f3..d2701c53ed68 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -203,34 +203,6 @@ static int marvell_config_aneg(struct phy_device *phydev)
{
int err;
- /* The Marvell PHY has an errata which requires
- * that certain registers get written in order
- * to restart autonegotiation */
- err = phy_write(phydev, MII_BMCR, BMCR_RESET);
-
- if (err < 0)
- return err;
-
- err = phy_write(phydev, 0x1d, 0x1f);
- if (err < 0)
- return err;
-
- err = phy_write(phydev, 0x1e, 0x200c);
- if (err < 0)
- return err;
-
- err = phy_write(phydev, 0x1d, 0x5);
- if (err < 0)
- return err;
-
- err = phy_write(phydev, 0x1e, 0);
- if (err < 0)
- return err;
-
- err = phy_write(phydev, 0x1e, 0x100);
- if (err < 0)
- return err;
-
err = marvell_set_polarity(phydev, phydev->mdix);
if (err < 0)
return err;
@@ -264,6 +236,42 @@ static int marvell_config_aneg(struct phy_device *phydev)
return 0;
}
+static int m88e1101_config_aneg(struct phy_device *phydev)
+{
+ int err;
+
+ /* This Marvell PHY has an errata which requires
+ * that certain registers get written in order
+ * to restart autonegotiation
+ */
+ err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x1f);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x200c);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x5);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x100);
+ if (err < 0)
+ return err;
+
+ return marvell_config_aneg(phydev);
+}
+
#ifdef CONFIG_OF_MDIO
/*
* Set and/or override some configuration registers based on the
@@ -993,7 +1001,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1101",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .config_aneg = &marvell_config_aneg,
+ .config_aneg = &m88e1101_config_aneg,
.read_status = &genphy_read_status,
.ack_interrupt = &marvell_ack_interrupt,
.config_intr = &marvell_config_intr,
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 09052f9e324f..582d8f0c6266 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -730,6 +730,8 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx */
{QMI_FIXED_INTF(0x1199, 0x9079, 8)}, /* Sierra Wireless EM74xx */
{QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */
+ {QMI_FIXED_INTF(0x1199, 0x907b, 8)}, /* Sierra Wireless EM74xx */
+ {QMI_FIXED_INTF(0x1199, 0x907b, 10)}, /* Sierra Wireless EM74xx */
{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
{QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */
{QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */
@@ -754,6 +756,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/usb/r8152.c b/drivers/net/usb/r8152.c
index fbb1867ff25c..1c27e6fb99f9 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -1851,6 +1851,9 @@ static int r8152_poll(struct napi_struct *napi, int budget)
napi_complete(napi);
if (!list_empty(&tp->rx_done))
napi_schedule(napi);
+ else if (!skb_queue_empty(&tp->tx_queue) &&
+ !list_empty(&tp->tx_free))
+ napi_schedule(napi);
}
return work_done;
@@ -2990,10 +2993,13 @@ static void set_carrier(struct r8152 *tp)
if (!netif_carrier_ok(netdev)) {
tp->rtl_ops.enable(tp);
set_bit(RTL8152_SET_RX_MODE, &tp->flags);
+ netif_stop_queue(netdev);
napi_disable(&tp->napi);
netif_carrier_on(netdev);
rtl_start_rx(tp);
napi_enable(&tp->napi);
+ netif_wake_queue(netdev);
+ netif_info(tp, link, netdev, "carrier on\n");
}
} else {
if (netif_carrier_ok(netdev)) {
@@ -3001,6 +3007,7 @@ static void set_carrier(struct r8152 *tp)
napi_disable(&tp->napi);
tp->rtl_ops.disable(tp);
napi_enable(&tp->napi);
+ netif_info(tp, link, netdev, "carrier off\n");
}
}
}
@@ -3385,12 +3392,12 @@ static int rtl8152_pre_reset(struct usb_interface *intf)
if (!netif_running(netdev))
return 0;
+ netif_stop_queue(netdev);
napi_disable(&tp->napi);
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
cancel_delayed_work_sync(&tp->schedule);
if (netif_carrier_ok(netdev)) {
- netif_stop_queue(netdev);
mutex_lock(&tp->control);
tp->rtl_ops.disable(tp);
mutex_unlock(&tp->control);
@@ -3415,12 +3422,14 @@ static int rtl8152_post_reset(struct usb_interface *intf)
if (netif_carrier_ok(netdev)) {
mutex_lock(&tp->control);
tp->rtl_ops.enable(tp);
+ rtl_start_rx(tp);
rtl8152_set_rx_mode(netdev);
mutex_unlock(&tp->control);
- netif_wake_queue(netdev);
}
napi_enable(&tp->napi);
+ netif_wake_queue(netdev);
+ usb_submit_urb(tp->intr_urb, GFP_KERNEL);
return 0;
}
diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c
index a251588762ec..0b5a84c9022c 100644
--- a/drivers/net/usb/sierra_net.c
+++ b/drivers/net/usb/sierra_net.c
@@ -73,8 +73,6 @@ static atomic_t iface_counter = ATOMIC_INIT(0);
/* Private data structure */
struct sierra_net_data {
- u8 ethr_hdr_tmpl[ETH_HLEN]; /* ethernet header template for rx'd pkts */
-
u16 link_up; /* air link up or down */
u8 tx_hdr_template[4]; /* part of HIP hdr for tx'd packets */
@@ -122,6 +120,7 @@ struct param {
/* LSI Protocol types */
#define SIERRA_NET_PROTOCOL_UMTS 0x01
+#define SIERRA_NET_PROTOCOL_UMTS_DS 0x04
/* LSI Coverage */
#define SIERRA_NET_COVERAGE_NONE 0x00
#define SIERRA_NET_COVERAGE_NOPACKET 0x01
@@ -129,7 +128,8 @@ struct param {
/* LSI Session */
#define SIERRA_NET_SESSION_IDLE 0x00
/* LSI Link types */
-#define SIERRA_NET_AS_LINK_TYPE_IPv4 0x00
+#define SIERRA_NET_AS_LINK_TYPE_IPV4 0x00
+#define SIERRA_NET_AS_LINK_TYPE_IPV6 0x02
struct lsi_umts {
u8 protocol;
@@ -137,9 +137,14 @@ struct lsi_umts {
__be16 length;
/* eventually use a union for the rest - assume umts for now */
u8 coverage;
- u8 unused2[41];
+ u8 network_len; /* network name len */
+ u8 network[40]; /* network name (UCS2, bigendian) */
u8 session_state;
u8 unused3[33];
+} __packed;
+
+struct lsi_umts_single {
+ struct lsi_umts lsi;
u8 link_type;
u8 pdp_addr_len; /* NW-supplied PDP address len */
u8 pdp_addr[16]; /* NW-supplied PDP address (bigendian)) */
@@ -158,10 +163,31 @@ struct lsi_umts {
u8 reserved[8];
} __packed;
+struct lsi_umts_dual {
+ struct lsi_umts lsi;
+ u8 pdp_addr4_len; /* NW-supplied PDP IPv4 address len */
+ u8 pdp_addr4[4]; /* NW-supplied PDP IPv4 address (bigendian)) */
+ u8 pdp_addr6_len; /* NW-supplied PDP IPv6 address len */
+ u8 pdp_addr6[16]; /* NW-supplied PDP IPv6 address (bigendian)) */
+ u8 unused4[23];
+ u8 dns1_addr4_len; /* NW-supplied 1st DNS v4 address len (bigendian) */
+ u8 dns1_addr4[4]; /* NW-supplied 1st DNS v4 address */
+ u8 dns1_addr6_len; /* NW-supplied 1st DNS v6 address len */
+ u8 dns1_addr6[16]; /* NW-supplied 1st DNS v6 address (bigendian)*/
+ u8 dns2_addr4_len; /* NW-supplied 2nd DNS v4 address len (bigendian) */
+ u8 dns2_addr4[4]; /* NW-supplied 2nd DNS v4 address */
+ u8 dns2_addr6_len; /* NW-supplied 2nd DNS v6 address len */
+ u8 dns2_addr6[16]; /* NW-supplied 2nd DNS v6 address (bigendian)*/
+ u8 unused5[68];
+} __packed;
+
#define SIERRA_NET_LSI_COMMON_LEN 4
-#define SIERRA_NET_LSI_UMTS_LEN (sizeof(struct lsi_umts))
+#define SIERRA_NET_LSI_UMTS_LEN (sizeof(struct lsi_umts_single))
#define SIERRA_NET_LSI_UMTS_STATUS_LEN \
(SIERRA_NET_LSI_UMTS_LEN - SIERRA_NET_LSI_COMMON_LEN)
+#define SIERRA_NET_LSI_UMTS_DS_LEN (sizeof(struct lsi_umts_dual))
+#define SIERRA_NET_LSI_UMTS_DS_STATUS_LEN \
+ (SIERRA_NET_LSI_UMTS_DS_LEN - SIERRA_NET_LSI_COMMON_LEN)
/* Forward definitions */
static void sierra_sync_timer(unsigned long syncdata);
@@ -191,10 +217,11 @@ static inline void sierra_net_set_private(struct usbnet *dev,
dev->data[0] = (unsigned long)priv;
}
-/* is packet IPv4 */
+/* is packet IPv4/IPv6 */
static inline int is_ip(struct sk_buff *skb)
{
- return skb->protocol == cpu_to_be16(ETH_P_IP);
+ return skb->protocol == cpu_to_be16(ETH_P_IP) ||
+ skb->protocol == cpu_to_be16(ETH_P_IPV6);
}
/*
@@ -350,49 +377,54 @@ static inline int sierra_net_is_valid_addrlen(u8 len)
static int sierra_net_parse_lsi(struct usbnet *dev, char *data, int datalen)
{
struct lsi_umts *lsi = (struct lsi_umts *)data;
+ u32 expected_length;
- if (datalen < sizeof(struct lsi_umts)) {
- netdev_err(dev->net, "%s: Data length %d, exp %Zu\n",
- __func__, datalen,
- sizeof(struct lsi_umts));
+ if (datalen < sizeof(struct lsi_umts_single)) {
+ netdev_err(dev->net, "%s: Data length %d, exp >= %Zu\n",
+ __func__, datalen, sizeof(struct lsi_umts_single));
return -1;
}
- if (lsi->length != cpu_to_be16(SIERRA_NET_LSI_UMTS_STATUS_LEN)) {
- netdev_err(dev->net, "%s: LSI_UMTS_STATUS_LEN %d, exp %u\n",
- __func__, be16_to_cpu(lsi->length),
- (u32)SIERRA_NET_LSI_UMTS_STATUS_LEN);
- return -1;
+ /* Validate the session state */
+ if (lsi->session_state == SIERRA_NET_SESSION_IDLE) {
+ netdev_err(dev->net, "Session idle, 0x%02x\n",
+ lsi->session_state);
+ return 0;
}
/* Validate the protocol - only support UMTS for now */
- if (lsi->protocol != SIERRA_NET_PROTOCOL_UMTS) {
+ if (lsi->protocol == SIERRA_NET_PROTOCOL_UMTS) {
+ struct lsi_umts_single *single = (struct lsi_umts_single *)lsi;
+
+ /* Validate the link type */
+ if (single->link_type != SIERRA_NET_AS_LINK_TYPE_IPV4 &&
+ single->link_type != SIERRA_NET_AS_LINK_TYPE_IPV6) {
+ netdev_err(dev->net, "Link type unsupported: 0x%02x\n",
+ single->link_type);
+ return -1;
+ }
+ expected_length = SIERRA_NET_LSI_UMTS_STATUS_LEN;
+ } else if (lsi->protocol == SIERRA_NET_PROTOCOL_UMTS_DS) {
+ expected_length = SIERRA_NET_LSI_UMTS_DS_STATUS_LEN;
+ } else {
netdev_err(dev->net, "Protocol unsupported, 0x%02x\n",
- lsi->protocol);
+ lsi->protocol);
return -1;
}
- /* Validate the link type */
- if (lsi->link_type != SIERRA_NET_AS_LINK_TYPE_IPv4) {
- netdev_err(dev->net, "Link type unsupported: 0x%02x\n",
- lsi->link_type);
+ if (be16_to_cpu(lsi->length) != expected_length) {
+ netdev_err(dev->net, "%s: LSI_UMTS_STATUS_LEN %d, exp %u\n",
+ __func__, be16_to_cpu(lsi->length), expected_length);
return -1;
}
/* Validate the coverage */
- if (lsi->coverage == SIERRA_NET_COVERAGE_NONE
- || lsi->coverage == SIERRA_NET_COVERAGE_NOPACKET) {
+ if (lsi->coverage == SIERRA_NET_COVERAGE_NONE ||
+ lsi->coverage == SIERRA_NET_COVERAGE_NOPACKET) {
netdev_err(dev->net, "No coverage, 0x%02x\n", lsi->coverage);
return 0;
}
- /* Validate the session state */
- if (lsi->session_state == SIERRA_NET_SESSION_IDLE) {
- netdev_err(dev->net, "Session idle, 0x%02x\n",
- lsi->session_state);
- return 0;
- }
-
/* Set link_sense true */
return 1;
}
@@ -662,7 +694,6 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf)
u8 numendpoints;
u16 fwattr = 0;
int status;
- struct ethhdr *eth;
struct sierra_net_data *priv;
static const u8 sync_tmplate[sizeof(priv->sync_msg)] = {
0x00, 0x00, SIERRA_NET_HIP_MSYNC_ID, 0x00};
@@ -700,11 +731,6 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->dev_addr[ETH_ALEN-2] = atomic_inc_return(&iface_counter);
dev->net->dev_addr[ETH_ALEN-1] = ifacenum;
- /* we will have to manufacture ethernet headers, prepare template */
- eth = (struct ethhdr *)priv->ethr_hdr_tmpl;
- memcpy(&eth->h_dest, dev->net->dev_addr, ETH_ALEN);
- eth->h_proto = cpu_to_be16(ETH_P_IP);
-
/* prepare shutdown message template */
memcpy(priv->shdwn_msg, shdwn_tmplate, sizeof(priv->shdwn_msg));
/* set context index initially to 0 - prepares tx hdr template */
@@ -833,9 +859,14 @@ static int sierra_net_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
skb_pull(skb, hh.hdrlen);
- /* We are going to accept this packet, prepare it */
- memcpy(skb->data, sierra_net_get_private(dev)->ethr_hdr_tmpl,
- ETH_HLEN);
+ /* We are going to accept this packet, prepare it.
+ * In case protocol is IPv6, keep it, otherwise force IPv4.
+ */
+ skb_reset_mac_header(skb);
+ if (eth_hdr(skb)->h_proto != cpu_to_be16(ETH_P_IPV6))
+ eth_hdr(skb)->h_proto = cpu_to_be16(ETH_P_IP);
+ eth_zero_addr(eth_hdr(skb)->h_source);
+ memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN);
/* Last packet in batch handled by usbnet */
if (hh.payload_len.word == skb->len)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 0e2a19e58923..7f7c87762bc6 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1415,6 +1415,7 @@ static const struct net_device_ops virtnet_netdev = {
#ifdef CONFIG_NET_RX_BUSY_POLL
.ndo_busy_poll = virtnet_busy_poll,
#endif
+ .ndo_features_check = passthru_features_check,
};
static void virtnet_config_changed_work(struct work_struct *work)
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 590750ab6564..9a986ccd42e5 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -77,6 +77,8 @@ static const u8 all_zeros_mac[ETH_ALEN];
static int vxlan_sock_add(struct vxlan_dev *vxlan);
+static void vxlan_vs_del_dev(struct vxlan_dev *vxlan);
+
/* per-network namespace private data for this module */
struct vxlan_net {
struct list_head vxlan_list;
@@ -1052,6 +1054,8 @@ static void __vxlan_sock_release(struct vxlan_sock *vs)
static void vxlan_sock_release(struct vxlan_dev *vxlan)
{
+ vxlan_vs_del_dev(vxlan);
+
__vxlan_sock_release(vxlan->vn4_sock);
#if IS_ENABLED(CONFIG_IPV6)
__vxlan_sock_release(vxlan->vn6_sock);
@@ -2255,6 +2259,15 @@ static void vxlan_cleanup(unsigned long arg)
mod_timer(&vxlan->age_timer, next_timer);
}
+static void vxlan_vs_del_dev(struct vxlan_dev *vxlan)
+{
+ struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
+
+ spin_lock(&vn->sock_lock);
+ hlist_del_init_rcu(&vxlan->hlist);
+ spin_unlock(&vn->sock_lock);
+}
+
static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan)
{
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
@@ -3028,12 +3041,6 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev,
static void vxlan_dellink(struct net_device *dev, struct list_head *head)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
- struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
-
- spin_lock(&vn->sock_lock);
- if (!hlist_unhashed(&vxlan->hlist))
- hlist_del_rcu(&vxlan->hlist);
- spin_unlock(&vn->sock_lock);
gro_cells_destroy(&vxlan->gro_cells);
list_del(&vxlan->next);
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 486af5dac5df..2217e0cae4da 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -333,5 +333,6 @@ source "drivers/net/wireless/cw1200/Kconfig"
source "drivers/net/wireless/rsi/Kconfig"
source "drivers/net/wireless/cnss/Kconfig"
source "drivers/net/wireless/cnss_genl/Kconfig"
+source "drivers/net/wireless/cnss_utils/Kconfig"
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 0204fc00f0c5..704a976bdfb5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -67,3 +67,4 @@ obj-$(CONFIG_CNSS) += cnss/
obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/
obj-$(CONFIG_CNSS_CRYPTO) += cnss_crypto/
obj-$(CONFIG_CNSS_GENL) += cnss_genl/
+obj-$(CONFIG_CNSS_UTILS) += cnss_utils/
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 9acaffa51516..6906bddb229f 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1550,6 +1550,7 @@ static void ath10k_core_restart(struct work_struct *work)
complete(&ar->offchan_tx_completed);
complete(&ar->install_key_done);
complete(&ar->vdev_setup_done);
+ complete(&ar->vdev_delete_done);
complete(&ar->thermal.wmi_sync);
complete(&ar->bss_survey_done);
wake_up(&ar->htt.empty_tx_wq);
@@ -2373,8 +2374,10 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
init_completion(&ar->install_key_done);
init_completion(&ar->vdev_setup_done);
+ init_completion(&ar->vdev_delete_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..8ddbae96794d 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -365,7 +365,8 @@ struct ath10k_sta {
#endif
};
-#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5 * HZ)
+#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5 * HZ)
+#define ATH10K_VDEV_DELETE_TIMEOUT_HZ (5 * HZ)
enum ath10k_beacon_state {
ATH10K_BEACON_SCHEDULED = 0,
@@ -853,6 +854,7 @@ struct ath10k {
struct completion install_key_done;
struct completion vdev_setup_done;
+ struct completion vdev_delete_done;
struct workqueue_struct *workqueue;
/* Auxiliary workqueue */
@@ -957,7 +959,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..e430bab869a8 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,
+ 50 * HZ);
+
+ if (time_left == 0) {
+ ath10k_warn(ar, "Timeout in receiving peer delete response\n");
+ return -ETIMEDOUT;
+ }
+ }
+
ar->num_peers--;
return 0;
@@ -983,6 +994,7 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
arg.channel.max_antenna_gain = channel->max_antenna_gain * 2;
reinit_completion(&ar->vdev_setup_done);
+ reinit_completion(&ar->vdev_delete_done);
ret = ath10k_wmi_vdev_start(ar, &arg);
if (ret) {
@@ -1032,6 +1044,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar)
ar->monitor_vdev_id, ret);
reinit_completion(&ar->vdev_setup_done);
+ reinit_completion(&ar->vdev_delete_done);
ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
if (ret)
@@ -1338,6 +1351,7 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
lockdep_assert_held(&ar->conf_mutex);
reinit_completion(&ar->vdev_setup_done);
+ reinit_completion(&ar->vdev_delete_done);
ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
if (ret) {
@@ -1374,6 +1388,7 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif,
lockdep_assert_held(&ar->conf_mutex);
reinit_completion(&ar->vdev_setup_done);
+ reinit_completion(&ar->vdev_delete_done);
arg.vdev_id = arvif->vdev_id;
arg.dtim_period = arvif->dtim_period;
@@ -5112,6 +5127,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_peer *peer;
+ unsigned long time_left;
int ret;
int i;
@@ -5151,6 +5167,16 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n",
arvif->vdev_id, ret);
+ if (QCA_REV_WCN3990(ar)) {
+ time_left = wait_for_completion_timeout(
+ &ar->vdev_delete_done,
+ ATH10K_VDEV_DELETE_TIMEOUT_HZ);
+ if (time_left == 0) {
+ ath10k_warn(ar, "Timeout in receiving vdev delete resp\n");
+ return;
+ }
+ }
+
/* Some firmware revisions don't notify host about self-peer removal
* until after associated vdev is deleted.
*/
@@ -5204,6 +5230,26 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
mutex_unlock(&ar->conf_mutex);
}
+static int ath10k_change_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type, bool p2p)
+{
+ struct ath10k *ar = hw->priv;
+ int ret = 0;
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "change_interface new: %d (%d), old: %d (%d)\n", new_type,
+ p2p, vif->type, vif->p2p);
+
+ if (new_type != vif->type || vif->p2p != p2p) {
+ ath10k_remove_interface(hw, vif);
+ vif->type = new_type;
+ vif->p2p = p2p;
+ ret = ath10k_add_interface(hw, vif);
+ }
+ return ret;
+}
+
/*
* FIXME: Has to be verified.
*/
@@ -5924,6 +5970,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 &&
@@ -6584,7 +6633,8 @@ static int ath10k_get_survey(struct ieee80211_hw *hw, int idx,
goto exit;
}
- ath10k_mac_update_bss_chan_survey(ar, &sband->channels[idx]);
+ if (!QCA_REV_WCN3990(ar))
+ ath10k_mac_update_bss_chan_survey(ar, &sband->channels[idx]);
spin_lock_bh(&ar->data_lock);
memcpy(survey, ar_survey, sizeof(*survey));
@@ -7392,8 +7442,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 +7460,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);
@@ -7440,6 +7492,7 @@ static const struct ieee80211_ops ath10k_ops = {
.stop = ath10k_stop,
.config = ath10k_config,
.add_interface = ath10k_add_interface,
+ .change_interface = ath10k_change_interface,
.remove_interface = ath10k_remove_interface,
.configure_filter = ath10k_configure_filter,
.bss_info_changed = ath10k_bss_info_changed,
@@ -7736,6 +7789,81 @@ static struct ieee80211_iface_combination ath10k_tlv_qcs_if_comb[] = {
},
};
+static const struct ieee80211_iface_limit ath10k_wcn3990_if_limit[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
+ },
+};
+
+static const struct ieee80211_iface_limit ath10k_wcn3990_qcs_if_limit[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_P2P_CLIENT),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+ BIT(NL80211_IFTYPE_P2P_GO),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
+ },
+};
+
+static const struct ieee80211_iface_limit ath10k_wcn3990_if_limit_ibss[] = {
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_ADHOC),
+ },
+};
+
+static struct ieee80211_iface_combination ath10k_wcn3990_qcs_if_comb[] = {
+ {
+ .limits = ath10k_wcn3990_if_limit,
+ .num_different_channels = 1,
+ .max_interfaces = 4,
+ .n_limits = ARRAY_SIZE(ath10k_wcn3990_if_limit),
+ },
+ {
+ .limits = ath10k_wcn3990_qcs_if_limit,
+ .num_different_channels = 2,
+ .max_interfaces = 4,
+ .n_limits = ARRAY_SIZE(ath10k_wcn3990_qcs_if_limit),
+ },
+ {
+ .limits = ath10k_wcn3990_if_limit_ibss,
+ .num_different_channels = 1,
+ .max_interfaces = 2,
+ .n_limits = ARRAY_SIZE(ath10k_wcn3990_if_limit_ibss),
+ },
+};
+
static const struct ieee80211_iface_limit ath10k_10_4_if_limits[] = {
{
.max = 1,
@@ -7967,6 +8095,14 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
break;
case ATH10K_FW_WMI_OP_VERSION_TLV:
+ ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+ if (QCA_REV_WCN3990(ar)) {
+ ar->hw->wiphy->iface_combinations =
+ ath10k_wcn3990_qcs_if_comb;
+ ar->hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(ath10k_wcn3990_qcs_if_comb);
+ break;
+ }
if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) {
ar->hw->wiphy->iface_combinations =
ath10k_tlv_qcs_if_comb;
@@ -7977,7 +8113,6 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(ath10k_tlv_if_comb);
}
- ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
break;
case ATH10K_FW_WMI_OP_VERSION_10_1:
case ATH10K_FW_WMI_OP_VERSION_10_2:
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 75f2528b8b84..888ab5ad37f1 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 */
/***********/
@@ -468,6 +477,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_TLV_VDEV_STOPPED_EVENTID:
ath10k_wmi_event_vdev_stopped(ar, skb);
break;
+ case WMI_TLV_VDEV_DELETE_RESP_EVENTID:
+ ath10k_wmi_event_vdev_delete_resp(ar, skb);
+ break;
case WMI_TLV_PEER_STA_KICKOUT_EVENTID:
ath10k_wmi_event_peer_sta_kickout(ar, skb);
break;
@@ -552,6 +564,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;
@@ -669,6 +684,13 @@ static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar,
arg->noise_floor = ev->noise_floor;
arg->rx_clear_count = ev->rx_clear_count;
arg->cycle_count = ev->cycle_count;
+ arg->chan_tx_pwr_range = ev->chan_tx_pwr_range;
+ arg->chan_tx_pwr_tp = ev->chan_tx_pwr_tp;
+ arg->rx_frame_count = ev->rx_frame_count;
+ arg->my_bss_rx_cycle_count = ev->my_bss_rx_cycle_count;
+ arg->rx_11b_mode_data_duration = ev->rx_11b_mode_data_duration;
+ arg->tx_frame_cnt = ev->tx_frame_cnt;
+ arg->mac_clk_mhz = ev->mac_clk_mhz;
kfree(tb);
return 0;
@@ -1512,11 +1534,14 @@ ath10k_wmi_tlv_op_gen_start_scan(struct ath10k *ar,
cmd->ie_len = __cpu_to_le32(arg->ie_len);
cmd->num_probes = __cpu_to_le32(3);
- if (QCA_REV_WCN3990(ar))
+ if (QCA_REV_WCN3990(ar)) {
cmd->common.scan_ctrl_flags = ar->fw_flags->flags;
- else
+ cmd->common.scan_ctrl_flags |=
+ __cpu_to_le32(WMI_SCAN_CHAN_STAT_EVENT);
+ } else {
cmd->common.scan_ctrl_flags ^=
__cpu_to_le32(WMI_SCAN_FILTER_PROBE_REQ);
+ }
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
index 79f324f132e9..86b24c24d9f1 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -308,11 +308,15 @@ enum wmi_tlv_event_id {
WMI_TLV_VDEV_STOPPED_EVENTID,
WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
WMI_TLV_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID,
+ WMI_TLV_VDEV_TSF_REPORT_EVENTID,
+ WMI_TLV_VDEV_DELETE_RESP_EVENTID,
WMI_TLV_PEER_STA_KICKOUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PEER),
WMI_TLV_PEER_INFO_EVENTID,
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/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index cbbba8d79e6b..d60e7fbb7e74 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -2446,6 +2446,31 @@ static int ath10k_wmi_10_4_op_pull_ch_info_ev(struct ath10k *ar,
return 0;
}
+static void wlan_fill_survey_result(struct ath10k *ar,
+ struct survey_info *survey,
+ struct wmi_ch_info_ev_arg arg)
+{
+ u64 clock_freq;
+
+ if (!arg.mac_clk_mhz || !survey)
+ return;
+
+ clock_freq = arg.mac_clk_mhz * 1000;
+
+ memset(survey, 0, sizeof(*survey));
+
+ survey->noise = __le32_to_cpu(arg.noise_floor);
+ survey->time = __le32_to_cpu(arg.cycle_count) / clock_freq;
+ survey->time_busy = __le32_to_cpu(arg.rx_clear_count) / clock_freq;
+ survey->time_tx = __le32_to_cpu(arg.rx_clear_count) / clock_freq;
+
+ survey->filled = SURVEY_INFO_NOISE_DBM;
+ ar->ch_info_can_report_survey = true;
+
+ survey->filled |= (SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY |
+ SURVEY_INFO_TIME_TX);
+}
+
void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_ch_info_ev_arg arg = {};
@@ -2490,6 +2515,12 @@ void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
goto exit;
}
+ if (QCA_REV_WCN3990(ar)) {
+ survey = &ar->survey[idx];
+ wlan_fill_survey_result(ar, survey, arg);
+ goto exit;
+ }
+
if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
if (ar->ch_info_can_report_survey) {
survey = &ar->survey[idx];
@@ -3120,6 +3151,12 @@ void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb)
complete(&ar->vdev_setup_done);
}
+void ath10k_wmi_event_vdev_delete_resp(struct ath10k *ar, struct sk_buff *skb)
+{
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_DELETE_RESP_EVENTID\n");
+ complete(&ar->vdev_delete_done);
+}
+
static int
ath10k_wmi_op_pull_peer_kick_ev(struct ath10k *ar, struct sk_buff *skb,
struct wmi_peer_kick_ev_arg *arg)
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 74398b45e365..9bd374910379 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -6034,6 +6034,13 @@ struct wmi_chan_info_event {
__le32 noise_floor;
__le32 rx_clear_count;
__le32 cycle_count;
+ __le32 chan_tx_pwr_range;
+ __le32 chan_tx_pwr_tp;
+ __le32 rx_frame_count;
+ __le32 my_bss_rx_cycle_count;
+ __le32 rx_11b_mode_data_duration;
+ __le32 tx_frame_cnt;
+ __le32 mac_clk_mhz;
} __packed;
struct wmi_10_4_chan_info_event {
@@ -6247,6 +6254,10 @@ struct wmi_ch_info_ev_arg {
__le32 chan_tx_pwr_range;
__le32 chan_tx_pwr_tp;
__le32 rx_frame_count;
+ __le32 my_bss_rx_cycle_count;
+ __le32 rx_11b_mode_data_duration;
+ __le32 tx_frame_cnt;
+ __le32 mac_clk_mhz;
};
struct wmi_vdev_start_ev_arg {
@@ -6599,6 +6610,7 @@ int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb);
void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb);
void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb);
void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_vdev_delete_resp(struct ath10k *ar, struct sk_buff *skb);
void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb);
void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb);
void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb);
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/ath/regd.c b/drivers/net/wireless/ath/regd.c
index 06ea6cc9e30a..096818610d40 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -38,28 +38,28 @@ static int __ath_regd_init(struct ath_regulatory *reg);
/* We enable active scan on these a case by case basis by regulatory domain */
#define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\
NL80211_RRF_NO_IR)
-#define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\
+#define ATH9K_2GHZ_CH14 REG_RULE(2484 - 10, 2484 + 10, 20, 0, 20,\
NL80211_RRF_NO_IR | \
NL80211_RRF_NO_OFDM)
/* We allow IBSS on these on a case by case basis by regulatory domain */
-#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\
+#define ATH9K_5GHZ_5180_5320 REG_RULE(5180 - 10, 5320 + 10, 160, 0, 20,\
NL80211_RRF_NO_IR)
-#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\
+#define ATH9K_5GHZ_5500_5825 REG_RULE(5500 - 10, 5825 + 10, 80, 0, 20,\
NL80211_RRF_NO_IR)
-#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\
+#define ATH9K_5GHZ_5745_5825 REG_RULE(5745 - 10, 5825 + 10, 80, 0, 20,\
NL80211_RRF_NO_IR)
#define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \
ATH9K_2GHZ_CH12_13, \
ATH9K_2GHZ_CH14
-#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \
- ATH9K_5GHZ_5470_5850
+#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5180_5320, \
+ ATH9K_5GHZ_5500_5825
/* This one skips what we call "mid band" */
-#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \
- ATH9K_5GHZ_5725_5850
+#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5180_5320, \
+ ATH9K_5GHZ_5745_5825
/* Can be used for:
* 0x60, 0x61, 0x62 */
@@ -256,7 +256,7 @@ EXPORT_SYMBOL(ath_is_49ghz_allowed);
/* Frequency is one where radar detection is required */
static bool ath_is_radar_freq(u16 center_freq)
{
- return (center_freq >= 5260 && center_freq <= 5700);
+ return (center_freq >= 5260 && center_freq <= 5720);
}
static void ath_force_clear_no_ir_chan(struct wiphy *wiphy,
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 69214fb2586c..63bb7686b811 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -16,6 +16,7 @@
#include <linux/etherdevice.h>
#include <linux/moduleparam.h>
+#include <net/netlink.h>
#include "wil6210.h"
#include "wmi.h"
#include "ftm.h"
@@ -56,6 +57,62 @@ static struct ieee80211_channel wil_60ghz_channels[] = {
#define QCA_NL80211_VENDOR_ID 0x001374
+#define WIL_MAX_RF_SECTORS (128)
+#define WIL_CID_ALL (0xff)
+
+enum qca_wlan_vendor_attr_rf_sector {
+ QCA_ATTR_MAC_ADDR = 6,
+ QCA_ATTR_PAD = 13,
+ QCA_ATTR_TSF = 29,
+ QCA_ATTR_DMG_RF_SECTOR_INDEX = 30,
+ QCA_ATTR_DMG_RF_SECTOR_TYPE = 31,
+ QCA_ATTR_DMG_RF_MODULE_MASK = 32,
+ QCA_ATTR_DMG_RF_SECTOR_CFG = 33,
+ QCA_ATTR_DMG_RF_SECTOR_MAX,
+};
+
+enum qca_wlan_vendor_attr_dmg_rf_sector_type {
+ QCA_ATTR_DMG_RF_SECTOR_TYPE_RX,
+ QCA_ATTR_DMG_RF_SECTOR_TYPE_TX,
+ QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX
+};
+
+enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
+ QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
+
+ /* keep last */
+ QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_MAX =
+ QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
+};
+
+static const struct
+nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = {
+ [QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN },
+ [QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 },
+ [QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 },
+ [QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED },
+};
+
+static const struct
+nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = {
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 },
+};
+
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128,
QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129,
@@ -66,8 +123,25 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134,
QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135,
QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
};
+static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len);
+static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len);
+static int wil_rf_sector_get_selected(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len);
+static int wil_rf_sector_set_selected(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len);
+
/* vendor specific commands */
static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = {
{
@@ -112,6 +186,36 @@ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = {
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wil_aoa_abort_measurement
},
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_rf_sector_get_cfg
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_rf_sector_set_cfg
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd =
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_rf_sector_get_selected
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd =
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_rf_sector_set_selected
+ },
};
/* vendor specific events */
@@ -856,6 +960,9 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf,
len, true);
+ if (len < sizeof(struct ieee80211_mgmt))
+ return -EINVAL;
+
cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
if (!cmd) {
rc = -ENOMEM;
@@ -1838,3 +1945,449 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil)
kfree(p2p_wdev);
}
}
+
+static int wil_rf_sector_status_to_rc(u8 status)
+{
+ switch (status) {
+ case WMI_RF_SECTOR_STATUS_SUCCESS:
+ return 0;
+ case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR:
+ return -EINVAL;
+ case WMI_RF_SECTOR_STATUS_BUSY_ERROR:
+ return -EAGAIN;
+ case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
+ int rc;
+ struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
+ u16 sector_index;
+ u8 sector_type;
+ u32 rf_modules_vec;
+ struct wmi_get_rf_sector_params_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_get_rf_sector_params_done_event evt;
+ } __packed reply;
+ struct sk_buff *msg;
+ struct nlattr *nl_cfgs, *nl_cfg;
+ u32 i;
+ struct wmi_rf_sector_info *si;
+
+ if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len,
+ wil_rf_sector_policy);
+ if (rc) {
+ wil_err(wil, "Invalid rf sector ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
+ !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
+ !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) {
+ wil_err(wil, "Invalid rf sector spec\n");
+ return -EINVAL;
+ }
+
+ sector_index = nla_get_u16(
+ tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
+ if (sector_index >= WIL_MAX_RF_SECTORS) {
+ wil_err(wil, "Invalid sector index %d\n", sector_index);
+ return -EINVAL;
+ }
+
+ sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
+ if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
+ wil_err(wil, "Invalid sector type %d\n", sector_type);
+ return -EINVAL;
+ }
+
+ rf_modules_vec = nla_get_u32(
+ tb[QCA_ATTR_DMG_RF_MODULE_MASK]);
+ if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) {
+ wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec);
+ return -EINVAL;
+ }
+
+ cmd.sector_idx = cpu_to_le16(sector_index);
+ cmd.sector_type = sector_type;
+ cmd.rf_modules_vec = rf_modules_vec & 0xFF;
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd),
+ WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
+ &reply, sizeof(reply),
+ 500);
+ if (rc)
+ return rc;
+ if (reply.evt.status) {
+ wil_err(wil, "get rf sector cfg failed with status %d\n",
+ reply.evt.status);
+ return wil_rf_sector_status_to_rc(reply.evt.status);
+ }
+
+ msg = cfg80211_vendor_cmd_alloc_reply_skb(
+ wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nla_put_u64(msg, QCA_ATTR_TSF,
+ le64_to_cpu(reply.evt.tsf)))
+ goto nla_put_failure;
+
+ nl_cfgs = nla_nest_start(msg, QCA_ATTR_DMG_RF_SECTOR_CFG);
+ if (!nl_cfgs)
+ goto nla_put_failure;
+ for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) {
+ if (!(rf_modules_vec & BIT(i)))
+ continue;
+ nl_cfg = nla_nest_start(msg, i);
+ if (!nl_cfg)
+ goto nla_put_failure;
+ si = &reply.evt.sectors_info[i];
+ if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
+ i) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
+ le32_to_cpu(si->etype0)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
+ le32_to_cpu(si->etype1)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
+ le32_to_cpu(si->etype2)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
+ le32_to_cpu(si->psh_hi)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
+ le32_to_cpu(si->psh_lo)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
+ le32_to_cpu(si->dtype_swch_off)))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_cfg);
+ }
+
+ nla_nest_end(msg, nl_cfgs);
+ rc = cfg80211_vendor_cmd_reply(msg);
+ return rc;
+nla_put_failure:
+ kfree_skb(msg);
+ return -ENOBUFS;
+}
+
+static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
+ int rc, tmp;
+ struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
+ struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1];
+ u16 sector_index, rf_module_index;
+ u8 sector_type;
+ u32 rf_modules_vec = 0;
+ struct wmi_set_rf_sector_params_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_set_rf_sector_params_done_event evt;
+ } __packed reply;
+ struct nlattr *nl_cfg;
+ struct wmi_rf_sector_info *si;
+
+ if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len,
+ wil_rf_sector_policy);
+ if (rc) {
+ wil_err(wil, "Invalid rf sector ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
+ !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
+ !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) {
+ wil_err(wil, "Invalid rf sector spec\n");
+ return -EINVAL;
+ }
+
+ sector_index = nla_get_u16(
+ tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
+ if (sector_index >= WIL_MAX_RF_SECTORS) {
+ wil_err(wil, "Invalid sector index %d\n", sector_index);
+ return -EINVAL;
+ }
+
+ sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
+ if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
+ wil_err(wil, "Invalid sector type %d\n", sector_type);
+ return -EINVAL;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.sector_idx = cpu_to_le16(sector_index);
+ cmd.sector_type = sector_type;
+ nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG],
+ tmp) {
+ rc = nla_parse_nested(tb2, QCA_ATTR_DMG_RF_SECTOR_CFG_MAX,
+ nl_cfg, wil_rf_sector_cfg_policy);
+ if (rc) {
+ wil_err(wil, "invalid sector cfg\n");
+ return -EINVAL;
+ }
+
+ if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) {
+ wil_err(wil, "missing cfg params\n");
+ return -EINVAL;
+ }
+
+ rf_module_index = nla_get_u8(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]);
+ if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) {
+ wil_err(wil, "invalid RF module index %d\n",
+ rf_module_index);
+ return -EINVAL;
+ }
+ rf_modules_vec |= BIT(rf_module_index);
+ si = &cmd.sectors_info[rf_module_index];
+ si->etype0 = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0]));
+ si->etype1 = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1]));
+ si->etype2 = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2]));
+ si->psh_hi = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI]));
+ si->psh_lo = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO]));
+ si->dtype_swch_off = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]));
+ }
+
+ cmd.rf_modules_vec = rf_modules_vec & 0xFF;
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd),
+ WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
+ &reply, sizeof(reply),
+ 500);
+ if (rc)
+ return rc;
+ return wil_rf_sector_status_to_rc(reply.evt.status);
+}
+
+static int wil_rf_sector_get_selected(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
+ int rc;
+ struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
+ u8 sector_type, mac_addr[ETH_ALEN];
+ int cid = 0;
+ struct wmi_get_selected_rf_sector_index_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_get_selected_rf_sector_index_done_event evt;
+ } __packed reply;
+ struct sk_buff *msg;
+
+ if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len,
+ wil_rf_sector_policy);
+ if (rc) {
+ wil_err(wil, "Invalid rf sector ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
+ wil_err(wil, "Invalid rf sector spec\n");
+ return -EINVAL;
+ }
+ sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
+ if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
+ wil_err(wil, "Invalid sector type %d\n", sector_type);
+ return -EINVAL;
+ }
+
+ if (tb[QCA_ATTR_MAC_ADDR]) {
+ ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
+ cid = wil_find_cid(wil, mac_addr);
+ if (cid < 0) {
+ wil_err(wil, "invalid MAC address %pM\n", mac_addr);
+ return -ENOENT;
+ }
+ } else {
+ if (test_bit(wil_status_fwconnected, wil->status)) {
+ wil_err(wil, "must specify MAC address when connected\n");
+ return -EINVAL;
+ }
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cid = (u8)cid;
+ cmd.sector_type = sector_type;
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID,
+ &cmd, sizeof(cmd),
+ WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
+ &reply, sizeof(reply),
+ 500);
+ if (rc)
+ return rc;
+ if (reply.evt.status) {
+ wil_err(wil, "get rf selected sector cfg failed with status %d\n",
+ reply.evt.status);
+ return wil_rf_sector_status_to_rc(reply.evt.status);
+ }
+
+ msg = cfg80211_vendor_cmd_alloc_reply_skb(
+ wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nla_put_u64(msg, QCA_ATTR_TSF,
+ le64_to_cpu(reply.evt.tsf)) ||
+ nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX,
+ le16_to_cpu(reply.evt.sector_idx)))
+ goto nla_put_failure;
+
+ rc = cfg80211_vendor_cmd_reply(msg);
+ return rc;
+nla_put_failure:
+ kfree_skb(msg);
+ return -ENOBUFS;
+}
+
+static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
+ u16 sector_index,
+ u8 sector_type, u8 cid)
+{
+ struct wmi_set_selected_rf_sector_index_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_set_selected_rf_sector_index_done_event evt;
+ } __packed reply;
+ int rc;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.sector_idx = cpu_to_le16(sector_index);
+ cmd.sector_type = sector_type;
+ cmd.cid = (u8)cid;
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID,
+ &cmd, sizeof(cmd),
+ WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
+ &reply, sizeof(reply),
+ 500);
+ if (rc)
+ return rc;
+ return wil_rf_sector_status_to_rc(reply.evt.status);
+}
+
+static int wil_rf_sector_set_selected(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
+ int rc;
+ struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
+ u16 sector_index;
+ u8 sector_type, mac_addr[ETH_ALEN], i;
+ int cid = 0;
+
+ if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len,
+ wil_rf_sector_policy);
+ if (rc) {
+ wil_err(wil, "Invalid rf sector ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
+ !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
+ wil_err(wil, "Invalid rf sector spec\n");
+ return -EINVAL;
+ }
+
+ sector_index = nla_get_u16(
+ tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
+ if (sector_index >= WIL_MAX_RF_SECTORS &&
+ sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
+ wil_err(wil, "Invalid sector index %d\n", sector_index);
+ return -EINVAL;
+ }
+
+ sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
+ if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
+ wil_err(wil, "Invalid sector type %d\n", sector_type);
+ return -EINVAL;
+ }
+
+ if (tb[QCA_ATTR_MAC_ADDR]) {
+ ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
+ if (!is_broadcast_ether_addr(mac_addr)) {
+ cid = wil_find_cid(wil, mac_addr);
+ if (cid < 0) {
+ wil_err(wil, "invalid MAC address %pM\n",
+ mac_addr);
+ return -ENOENT;
+ }
+ } else {
+ if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
+ wil_err(wil, "broadcast MAC valid only with unlocking\n");
+ return -EINVAL;
+ }
+ cid = -1;
+ }
+ } else {
+ if (test_bit(wil_status_fwconnected, wil->status)) {
+ wil_err(wil, "must specify MAC address when connected\n");
+ return -EINVAL;
+ }
+ /* otherwise, using cid=0 for unassociated station */
+ }
+
+ if (cid >= 0) {
+ rc = wil_rf_sector_wmi_set_selected(wil, sector_index,
+ sector_type, cid);
+ } else {
+ /* unlock all cids */
+ rc = wil_rf_sector_wmi_set_selected(
+ wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type,
+ WIL_CID_ALL);
+ if (rc == -EINVAL) {
+ for (i = 0; i < WIL6210_MAX_CID; i++) {
+ rc = wil_rf_sector_wmi_set_selected(
+ wil, WMI_INVALID_RF_SECTOR_INDEX,
+ sector_type, i);
+ /* the FW will silently ignore and return
+ * success for unused cid, so abort the loop
+ * on any other error
+ */
+ if (rc) {
+ wil_err(wil, "unlock cid %d failed with status %d\n",
+ i, rc);
+ break;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index d3691c64d47a..6eefb9e61ec4 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -805,8 +805,12 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
struct wireless_dev *wdev = wil_to_wdev(wil);
struct cfg80211_mgmt_tx_params params;
int rc;
- void *frame = kmalloc(len, GFP_KERNEL);
+ void *frame;
+ if (!len)
+ return -EINVAL;
+
+ frame = kmalloc(len, GFP_KERNEL);
if (!frame)
return -ENOMEM;
diff --git a/drivers/net/wireless/ath/wil6210/ioctl.c b/drivers/net/wireless/ath/wil6210/ioctl.c
index bbdd232df3b7..f8d2c209482c 100644
--- a/drivers/net/wireless/ath/wil6210/ioctl.c
+++ b/drivers/net/wireless/ath/wil6210/ioctl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -54,7 +54,7 @@ static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr,
}
off = a - wil->csr;
- if (size >= WIL6210_MEM_SIZE - off) {
+ if (size >= wil->bar_size - off) {
wil_err(wil, "Requested block does not fit into memory: "
"off = 0x%08x size = 0x%08x\n", off, size);
return NULL;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 8d3d25333ba5..e91cddbacf24 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -198,16 +198,18 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
.ramdump = wil_platform_rop_ramdump,
.fw_recovery = wil_platform_rop_fw_recovery,
};
+ u32 bar_size = pci_resource_len(pdev, 0);
/* check HW */
dev_info(&pdev->dev, WIL_NAME
- " device found [%04x:%04x] (rev %x)\n",
- (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
-
- if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
- dev_err(&pdev->dev, "Not " WIL_NAME "? "
- "BAR0 size is %lu while expecting %lu\n",
- (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE);
+ " device found [%04x:%04x] (rev %x) bar size 0x%x\n",
+ (int)pdev->vendor, (int)pdev->device, (int)pdev->revision,
+ bar_size);
+
+ if ((bar_size < WIL6210_MIN_MEM_SIZE) ||
+ (bar_size > WIL6210_MAX_MEM_SIZE)) {
+ dev_err(&pdev->dev, "Unexpected BAR0 size 0x%x\n",
+ bar_size);
return -ENODEV;
}
@@ -220,6 +222,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
wil->pdev = pdev;
pci_set_drvdata(pdev, wil);
+ wil->bar_size = bar_size;
/* rollback to if_free */
wil->platform_handle =
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index b4faf8212348..ce1f384e7f8e 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -98,6 +98,10 @@ static int wil_resume_keep_radio_on(struct wil6210_priv *wil)
}
}
+ /* Wake all queues */
+ if (test_bit(wil_status_fwconnected, wil->status))
+ wil_update_net_queues_bh(wil, NULL, false);
+
out:
if (rc)
set_bit(wil_status_suspended, wil->status);
@@ -113,6 +117,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
/* Prevent handling of new tx and wmi commands */
set_bit(wil_status_suspending, wil->status);
+ wil_update_net_queues_bh(wil, NULL, true);
if (!wil_is_tx_idle(wil)) {
wil_dbg_pm(wil, "Pending TX data, reject suspend\n");
@@ -201,13 +206,17 @@ resume_after_fail:
clear_bit(wil_status_suspending, wil->status);
rc = wmi_resume(wil);
/* if resume succeeded, reject the suspend */
- if (!rc)
+ if (!rc) {
rc = -EBUSY;
-
+ if (test_bit(wil_status_fwconnected, wil->status))
+ wil_update_net_queues_bh(wil, NULL, false);
+ }
return rc;
reject_suspend:
clear_bit(wil_status_suspending, wil->status);
+ if (test_bit(wil_status_fwconnected, wil->status))
+ wil_update_net_queues_bh(wil, NULL, false);
return -EBUSY;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 439fe30936b6..8f1e79b425cf 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -1930,6 +1930,11 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil,
return;
}
+ /* Do not wake the queues in suspend flow */
+ if (test_bit(wil_status_suspending, wil->status) ||
+ test_bit(wil_status_suspended, wil->status))
+ return;
+
/* check wake */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
struct vring *cur_vring = &wil->vring_tx[i];
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index e76ec6ed9995..9525f521d215 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -59,7 +59,8 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1);
}
-#define WIL6210_MEM_SIZE (2*1024*1024UL)
+#define WIL6210_MIN_MEM_SIZE (2 * 1024 * 1024UL)
+#define WIL6210_MAX_MEM_SIZE (4 * 1024 * 1024UL)
#define WIL_TX_Q_LEN_DEFAULT (4000)
#define WIL_RX_RING_SIZE_ORDER_DEFAULT (10)
@@ -620,6 +621,7 @@ extern u8 led_polarity;
struct wil6210_priv {
struct pci_dev *pdev;
+ u32 bar_size;
struct wireless_dev *wdev;
void __iomem *csr;
DECLARE_BITMAP(status, wil_status_last);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 24878ecf7e93..fba0d6a79ae2 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -160,7 +160,7 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
return NULL;
off = HOSTADDR(ptr);
- if (off > WIL6210_MEM_SIZE - 4)
+ if (off > wil->bar_size - 4)
return NULL;
return wil->csr + off;
@@ -180,7 +180,7 @@ void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr)
return NULL;
off = HOSTADDR(ptr);
- if (off > WIL6210_MEM_SIZE - 4)
+ if (off > wil->bar_size - 4)
return NULL;
return wil->csr + off;
diff --git a/drivers/net/wireless/cnss/Kconfig b/drivers/net/wireless/cnss/Kconfig
index 8e69a2b469b9..8946f65df716 100644
--- a/drivers/net/wireless/cnss/Kconfig
+++ b/drivers/net/wireless/cnss/Kconfig
@@ -1,5 +1,6 @@
config CNSS
tristate "CNSS driver for wifi module"
+ select CNSS_UTILS
select CRYPTO
select CRYPTO_HASH
select CRYPTO_BLKCIPHER
diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c
index 48d358c4722a..22c59d8c3c45 100644
--- a/drivers/net/wireless/cnss/cnss_pci.c
+++ b/drivers/net/wireless/cnss/cnss_pci.c
@@ -134,6 +134,7 @@ static DEFINE_SPINLOCK(pci_link_down_lock);
#define FW_IMAGE_MISSION (0x02)
#define FW_IMAGE_BDATA (0x03)
#define FW_IMAGE_PRINT (0x04)
+#define FW_SETUP_DELAY 2000
#define SEG_METADATA (0x01)
#define SEG_NON_PAGED (0x02)
@@ -244,7 +245,7 @@ static struct cnss_data {
struct pci_saved_state *saved_state;
u16 revision_id;
bool recovery_in_progress;
- bool fw_available;
+ atomic_t fw_available;
struct codeswap_codeseg_info *cnss_seg_info;
/* Virtual Address of the DMA page */
void *codeseg_cpuaddr[CODESWAP_MAX_CODESEGS];
@@ -273,6 +274,10 @@ static struct cnss_data {
u32 fw_dma_size;
u32 fw_seg_count;
struct segment_memory fw_seg_mem[MAX_NUM_OF_SEGMENTS];
+ atomic_t fw_store_in_progress;
+ /* Firmware setup complete lock */
+ struct mutex fw_setup_stat_lock;
+ struct completion fw_setup_complete;
void *bdata_cpu;
dma_addr_t bdata_dma;
u32 bdata_dma_size;
@@ -1369,10 +1374,21 @@ int cnss_get_fw_image(struct image_desc_info *image_desc_info)
!penv->fw_seg_count || !penv->bdata_seg_count)
return -EINVAL;
+ /* Check for firmware setup trigger by usersapce is in progress
+ * and wait for complition of firmware setup.
+ */
+
+ if (atomic_read(&penv->fw_store_in_progress)) {
+ wait_for_completion_timeout(&penv->fw_setup_complete,
+ msecs_to_jiffies(FW_SETUP_DELAY));
+ }
+
+ mutex_lock(&penv->fw_setup_stat_lock);
image_desc_info->fw_addr = penv->fw_dma;
image_desc_info->fw_size = penv->fw_dma_size;
image_desc_info->bdata_addr = penv->bdata_dma;
image_desc_info->bdata_size = penv->bdata_dma_size;
+ mutex_unlock(&penv->fw_setup_stat_lock);
return 0;
}
@@ -1552,7 +1568,7 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev,
penv->pdev = pdev;
penv->id = id;
- penv->fw_available = false;
+ atomic_set(&penv->fw_available, 0);
penv->device_id = pdev->device;
if (penv->smmu_iova_len) {
@@ -1858,8 +1874,19 @@ static ssize_t fw_image_setup_store(struct device *dev,
if (!penv)
return -ENODEV;
- if (sscanf(buf, "%d", &val) != 1)
+ if (atomic_read(&penv->fw_store_in_progress)) {
+ pr_info("%s: Firmware setup in progress\n", __func__);
+ return 0;
+ }
+
+ atomic_set(&penv->fw_store_in_progress, 1);
+ init_completion(&penv->fw_setup_complete);
+
+ if (kstrtoint(buf, 0, &val)) {
+ atomic_set(&penv->fw_store_in_progress, 0);
+ complete(&penv->fw_setup_complete);
return -EINVAL;
+ }
if (val == FW_IMAGE_FTM || val == FW_IMAGE_MISSION
|| val == FW_IMAGE_BDATA) {
@@ -1868,6 +1895,8 @@ static ssize_t fw_image_setup_store(struct device *dev,
if (ret != 0) {
pr_err("%s: Invalid parsing of FW image files %d",
__func__, ret);
+ atomic_set(&penv->fw_store_in_progress, 0);
+ complete(&penv->fw_setup_complete);
return -EINVAL;
}
penv->fw_image_setup = val;
@@ -1877,6 +1906,9 @@ static ssize_t fw_image_setup_store(struct device *dev,
penv->bmi_test = val;
}
+ atomic_set(&penv->fw_store_in_progress, 0);
+ complete(&penv->fw_setup_complete);
+
return count;
}
@@ -1979,7 +2011,7 @@ int cnss_get_codeswap_struct(struct codeswap_codeseg_info *swap_seg)
swap_seg = NULL;
return -ENOENT;
}
- if (!penv->fw_available) {
+ if (!atomic_read(&penv->fw_available)) {
pr_debug("%s: fw is not available\n", __func__);
return -ENOENT;
}
@@ -2003,6 +2035,16 @@ static void cnss_wlan_memory_expansion(void)
u_int32_t total_length = 0;
struct pci_dev *pdev;
+ /* Check for firmware setup trigger by usersapce is in progress
+ * and wait for complition of firmware setup.
+ */
+
+ if (atomic_read(&penv->fw_store_in_progress)) {
+ wait_for_completion_timeout(&penv->fw_setup_complete,
+ msecs_to_jiffies(FW_SETUP_DELAY));
+ }
+
+ mutex_lock(&penv->fw_setup_stat_lock);
filename = cnss_wlan_get_evicted_data_file();
pdev = penv->pdev;
dev = &pdev->dev;
@@ -2010,21 +2052,25 @@ static void cnss_wlan_memory_expansion(void)
if (!cnss_seg_info) {
pr_debug("cnss: cnss_seg_info is NULL\n");
+ mutex_unlock(&penv->fw_setup_stat_lock);
goto end;
}
- if (penv->fw_available) {
+ if (atomic_read(&penv->fw_available)) {
pr_debug("cnss: fw code already copied to host memory\n");
+ mutex_unlock(&penv->fw_setup_stat_lock);
goto end;
}
if (request_firmware(&fw_entry, filename, dev) != 0) {
pr_debug("cnss: failed to get fw: %s\n", filename);
+ mutex_unlock(&penv->fw_setup_stat_lock);
goto end;
}
if (!fw_entry || !fw_entry->data) {
pr_err("%s: INVALID FW entries\n", __func__);
+ mutex_unlock(&penv->fw_setup_stat_lock);
goto release_fw;
}
@@ -2059,7 +2105,9 @@ static void cnss_wlan_memory_expansion(void)
}
pr_debug("cnss: total_bytes copied: %d\n", total_length);
cnss_seg_info->codeseg_total_bytes = total_length;
- penv->fw_available = 1;
+
+ atomic_set(&penv->fw_available, 1);
+ mutex_unlock(&penv->fw_setup_stat_lock);
release_fw:
release_firmware(fw_entry);
@@ -2968,6 +3016,8 @@ skip_ramdump:
memset(phys_to_virt(0), 0, SZ_4K);
#endif
+ atomic_set(&penv->fw_store_in_progress, 0);
+ mutex_init(&penv->fw_setup_stat_lock);
ret = device_create_file(dev, &dev_attr_fw_image_setup);
if (ret) {
pr_err("cnss: fw_image_setup sys file creation failed\n");
diff --git a/drivers/net/wireless/cnss_utils/Kconfig b/drivers/net/wireless/cnss_utils/Kconfig
new file mode 100644
index 000000000000..5f43e4872d65
--- /dev/null
+++ b/drivers/net/wireless/cnss_utils/Kconfig
@@ -0,0 +1,6 @@
+config CNSS_UTILS
+ bool "CNSS utilities support"
+ ---help---
+ Add CNSS utilities support for the WLAN driver module.
+ This feature enable wlan driver to use CNSS utilities APIs to set
+ and get wlan related information. \ No newline at end of file
diff --git a/drivers/net/wireless/cnss_utils/Makefile b/drivers/net/wireless/cnss_utils/Makefile
new file mode 100644
index 000000000000..0d1ed7ae939e
--- /dev/null
+++ b/drivers/net/wireless/cnss_utils/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CNSS_UTILS) += cnss_utils.o
diff --git a/drivers/net/wireless/cnss_utils/cnss_utils.c b/drivers/net/wireless/cnss_utils/cnss_utils.c
new file mode 100644
index 000000000000..a452900868c4
--- /dev/null
+++ b/drivers/net/wireless/cnss_utils/cnss_utils.c
@@ -0,0 +1,310 @@
+/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "cnss_utils: " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/etherdevice.h>
+#include <net/cnss_utils.h>
+
+#define CNSS_MAX_CH_NUM 45
+struct cnss_unsafe_channel_list {
+ u16 unsafe_ch_count;
+ u16 unsafe_ch_list[CNSS_MAX_CH_NUM];
+};
+
+struct cnss_dfs_nol_info {
+ void *dfs_nol_info;
+ u16 dfs_nol_info_len;
+};
+
+#define MAX_NO_OF_MAC_ADDR 4
+struct cnss_wlan_mac_addr {
+ u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
+ u32 no_of_mac_addr_set;
+};
+
+static struct cnss_utils_priv {
+ struct cnss_unsafe_channel_list unsafe_channel_list;
+ struct cnss_dfs_nol_info dfs_nol_info;
+ /* generic mutex for unsafe channel */
+ struct mutex unsafe_channel_list_lock;
+ /* generic spin-lock for dfs_nol info */
+ spinlock_t dfs_nol_info_lock;
+ int driver_load_cnt;
+ bool is_wlan_mac_set;
+ struct cnss_wlan_mac_addr wlan_mac_addr;
+ enum cnss_utils_cc_src cc_source;
+} *cnss_utils_priv;
+
+int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
+ u16 *unsafe_ch_list, u16 ch_count)
+{
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+
+ if (!priv)
+ return -EINVAL;
+
+ mutex_lock(&priv->unsafe_channel_list_lock);
+ if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) {
+ mutex_unlock(&priv->unsafe_channel_list_lock);
+ return -EINVAL;
+ }
+
+ priv->unsafe_channel_list.unsafe_ch_count = ch_count;
+
+ if (ch_count == 0)
+ goto end;
+
+ memcpy(priv->unsafe_channel_list.unsafe_ch_list,
+ unsafe_ch_list, ch_count * sizeof(u16));
+
+end:
+ mutex_unlock(&priv->unsafe_channel_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel);
+
+int cnss_utils_get_wlan_unsafe_channel(struct device *dev,
+ u16 *unsafe_ch_list,
+ u16 *ch_count, u16 buf_len)
+{
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+
+ if (!priv)
+ return -EINVAL;
+
+ mutex_lock(&priv->unsafe_channel_list_lock);
+ if (!unsafe_ch_list || !ch_count) {
+ mutex_unlock(&priv->unsafe_channel_list_lock);
+ return -EINVAL;
+ }
+
+ if (buf_len <
+ (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
+ mutex_unlock(&priv->unsafe_channel_list_lock);
+ return -ENOMEM;
+ }
+
+ *ch_count = priv->unsafe_channel_list.unsafe_ch_count;
+ memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list,
+ priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16));
+ mutex_unlock(&priv->unsafe_channel_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel);
+
+int cnss_utils_wlan_set_dfs_nol(struct device *dev,
+ const void *info, u16 info_len)
+{
+ void *temp;
+ void *old_nol_info;
+ struct cnss_dfs_nol_info *dfs_info;
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+
+ if (!priv)
+ return -EINVAL;
+
+ if (!info || !info_len)
+ return -EINVAL;
+
+ temp = kmalloc(info_len, GFP_ATOMIC);
+ if (!temp)
+ return -ENOMEM;
+
+ memcpy(temp, info, info_len);
+ spin_lock_bh(&priv->dfs_nol_info_lock);
+ dfs_info = &priv->dfs_nol_info;
+ old_nol_info = dfs_info->dfs_nol_info;
+ dfs_info->dfs_nol_info = temp;
+ dfs_info->dfs_nol_info_len = info_len;
+ spin_unlock_bh(&priv->dfs_nol_info_lock);
+ kfree(old_nol_info);
+
+ return 0;
+}
+EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol);
+
+int cnss_utils_wlan_get_dfs_nol(struct device *dev,
+ void *info, u16 info_len)
+{
+ int len;
+ struct cnss_dfs_nol_info *dfs_info;
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+
+ if (!priv)
+ return -EINVAL;
+
+ if (!info || !info_len)
+ return -EINVAL;
+
+ spin_lock_bh(&priv->dfs_nol_info_lock);
+
+ dfs_info = &priv->dfs_nol_info;
+ if (!dfs_info->dfs_nol_info ||
+ dfs_info->dfs_nol_info_len == 0) {
+ spin_unlock_bh(&priv->dfs_nol_info_lock);
+ return -ENOENT;
+ }
+
+ len = min(info_len, dfs_info->dfs_nol_info_len);
+ memcpy(info, dfs_info->dfs_nol_info, len);
+ spin_unlock_bh(&priv->dfs_nol_info_lock);
+
+ return len;
+}
+EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol);
+
+void cnss_utils_increment_driver_load_cnt(struct device *dev)
+{
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+
+ if (!priv)
+ return;
+
+ ++(priv->driver_load_cnt);
+}
+EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt);
+
+int cnss_utils_get_driver_load_cnt(struct device *dev)
+{
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+
+ if (!priv)
+ return -EINVAL;
+
+ return priv->driver_load_cnt;
+}
+EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt);
+
+int cnss_utils_set_wlan_mac_address(const u8 *in, const uint32_t len)
+{
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+ u32 no_of_mac_addr;
+ struct cnss_wlan_mac_addr *addr = NULL;
+ int iter;
+ u8 *temp = NULL;
+
+ if (!priv)
+ return -EINVAL;
+
+ if (priv->is_wlan_mac_set) {
+ pr_debug("WLAN MAC address is already set\n");
+ return 0;
+ }
+
+ if (len == 0 || (len % ETH_ALEN) != 0) {
+ pr_err("Invalid length %d\n", len);
+ return -EINVAL;
+ }
+
+ no_of_mac_addr = len / ETH_ALEN;
+ if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
+ pr_err("Exceed maximum supported MAC address %u %u\n",
+ MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
+ return -EINVAL;
+ }
+
+ priv->is_wlan_mac_set = true;
+ addr = &priv->wlan_mac_addr;
+ addr->no_of_mac_addr_set = no_of_mac_addr;
+ temp = &addr->mac_addr[0][0];
+
+ for (iter = 0; iter < no_of_mac_addr;
+ ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
+ ether_addr_copy(temp, in);
+ pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ temp[0], temp[1], temp[2],
+ temp[3], temp[4], temp[5]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address);
+
+u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
+{
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+ struct cnss_wlan_mac_addr *addr = NULL;
+
+ if (!priv)
+ goto out;
+
+ if (!priv->is_wlan_mac_set) {
+ pr_debug("WLAN MAC address is not set\n");
+ goto out;
+ }
+
+ addr = &priv->wlan_mac_addr;
+ *num = addr->no_of_mac_addr_set;
+ return &addr->mac_addr[0][0];
+out:
+ *num = 0;
+ return NULL;
+}
+EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address);
+
+void cnss_utils_set_cc_source(struct device *dev,
+ enum cnss_utils_cc_src cc_source)
+{
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+
+ if (!priv)
+ return;
+
+ priv->cc_source = cc_source;
+}
+EXPORT_SYMBOL(cnss_utils_set_cc_source);
+
+enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev)
+{
+ struct cnss_utils_priv *priv = cnss_utils_priv;
+
+ if (!priv)
+ return -EINVAL;
+
+ return priv->cc_source;
+}
+EXPORT_SYMBOL(cnss_utils_get_cc_source);
+
+static int __init cnss_utils_init(void)
+{
+ struct cnss_utils_priv *priv = NULL;
+
+ priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->cc_source = CNSS_UTILS_SOURCE_CORE;
+
+ mutex_init(&priv->unsafe_channel_list_lock);
+ spin_lock_init(&priv->dfs_nol_info_lock);
+
+ cnss_utils_priv = priv;
+
+ return 0;
+}
+
+static void __exit cnss_utils_exit(void)
+{
+ kfree(cnss_utils_priv);
+ cnss_utils_priv = NULL;
+}
+
+module_init(cnss_utils_init);
+module_exit(cnss_utils_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(DEVICE "CNSS Utilities Driver");
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/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 1f445f357da1..888e9cfef51a 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -304,7 +304,7 @@ static void xennet_alloc_rx_buffers(struct netfront_queue *queue)
queue->rx_skbs[id] = skb;
ref = gnttab_claim_grant_reference(&queue->gref_rx_head);
- BUG_ON((signed short)ref < 0);
+ WARN_ON_ONCE(IS_ERR_VALUE((unsigned long)(int)ref));
queue->grant_rx_ref[id] = ref;
page = skb_frag_page(&skb_shinfo(skb)->frags[0]);
@@ -437,7 +437,7 @@ static void xennet_tx_setup_grant(unsigned long gfn, unsigned int offset,
id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs);
tx = RING_GET_REQUEST(&queue->tx, queue->tx.req_prod_pvt++);
ref = gnttab_claim_grant_reference(&queue->gref_tx_head);
- BUG_ON((signed short)ref < 0);
+ WARN_ON_ONCE(IS_ERR_VALUE((unsigned long)(int)ref));
gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id,
gfn, GNTMAP_readonly);
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/parport/parport_gsc.c b/drivers/parport/parport_gsc.c
index 6e3a60c78873..50f3bb0dd1f1 100644
--- a/drivers/parport/parport_gsc.c
+++ b/drivers/parport/parport_gsc.c
@@ -293,7 +293,7 @@ struct parport *parport_gsc_probe_port(unsigned long base,
p->irq = PARPORT_IRQ_NONE;
}
if (p->irq != PARPORT_IRQ_NONE) {
- printk(", irq %d", p->irq);
+ pr_cont(", irq %d", p->irq);
if (p->dma == PARPORT_DMA_AUTO) {
p->dma = PARPORT_DMA_NONE;
@@ -303,8 +303,8 @@ struct parport *parport_gsc_probe_port(unsigned long base,
is mandatory (see above) */
p->dma = PARPORT_DMA_NONE;
- printk(" [");
-#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
+ pr_cont(" [");
+#define printmode(x) {if(p->modes&PARPORT_MODE_##x){pr_cont("%s%s",f?",":"",#x);f++;}}
{
int f = 0;
printmode(PCSPP);
@@ -315,7 +315,7 @@ struct parport *parport_gsc_probe_port(unsigned long base,
// printmode(DMA);
}
#undef printmode
- printk("]\n");
+ pr_cont("]\n");
if (p->irq != PARPORT_IRQ_NONE) {
if (request_irq (p->irq, parport_irq_handler,
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/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 39400dda27c2..63ec68e6ac2a 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -552,7 +552,6 @@ static void armpmu_init(struct arm_pmu *armpmu)
.stop = armpmu_stop,
.read = armpmu_read,
.filter_match = armpmu_filter_match,
- .events_across_hotplug = 1,
};
}
diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.h b/drivers/phy/phy-qcom-ufs-qmp-v3.h
index 757ed7464e44..8cb4b0eeb866 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-v3.h
+++ b/drivers/phy/phy-qcom-ufs-qmp-v3.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -259,9 +259,9 @@ 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, 0x6C),
+ UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03),
@@ -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,10 +336,10 @@ 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, 0x6C),
+ UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03),
diff --git a/drivers/pinctrl/berlin/berlin-bg4ct.c b/drivers/pinctrl/berlin/berlin-bg4ct.c
index 09172043d589..c617ec49e9ed 100644
--- a/drivers/pinctrl/berlin/berlin-bg4ct.c
+++ b/drivers/pinctrl/berlin/berlin-bg4ct.c
@@ -217,7 +217,7 @@ static const struct berlin_desc_group berlin4ct_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_GROUP("SCRD0_CRD_PRES", 0xc, 0x3, 0x15,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO20 */
BERLIN_PINCTRL_FUNCTION(0x1, "scrd0"), /* crd pres */
- BERLIN_PINCTRL_FUNCTION(0x1, "sd1a")), /* DAT3 */
+ BERLIN_PINCTRL_FUNCTION(0x3, "sd1a")), /* DAT3 */
BERLIN_PINCTRL_GROUP("SPI1_SS0n", 0xc, 0x3, 0x18,
BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS0n */
BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO37 */
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index 08bffc344429..b111a5904952 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -1319,6 +1319,35 @@ int gsi_query_evt_ring_db_addr(unsigned long evt_ring_hdl,
}
EXPORT_SYMBOL(gsi_query_evt_ring_db_addr);
+int gsi_ring_evt_ring_db(unsigned long evt_ring_hdl, uint64_t value)
+{
+ struct gsi_evt_ctx *ctx;
+
+ if (!gsi_ctx) {
+ pr_err("%s:%d gsi context not allocated\n", __func__, __LINE__);
+ return -GSI_STATUS_NODEV;
+ }
+
+ if (evt_ring_hdl >= gsi_ctx->max_ev) {
+ GSIERR("bad params evt_ring_hdl=%lu\n", evt_ring_hdl);
+ return -GSI_STATUS_INVALID_PARAMS;
+ }
+
+ ctx = &gsi_ctx->evtr[evt_ring_hdl];
+
+ if (ctx->state != GSI_EVT_RING_STATE_ALLOCATED) {
+ GSIERR("bad state %d\n",
+ gsi_ctx->evtr[evt_ring_hdl].state);
+ return -GSI_STATUS_UNSUPPORTED_OP;
+ }
+
+ ctx->ring.wp_local = value;
+ gsi_ring_evt_doorbell(ctx);
+
+ return GSI_STATUS_SUCCESS;
+}
+EXPORT_SYMBOL(gsi_ring_evt_ring_db);
+
int gsi_reset_evt_ring(unsigned long evt_ring_hdl)
{
uint32_t val;
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
index a02247d3e938..9b3b53dcba68 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.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
@@ -1059,6 +1059,7 @@ static void ipa_mhi_gsi_ev_err_cb(struct gsi_evt_err_notify *notify)
IPA_MHI_ERR("Unexpected err evt: %d\n", notify->evt_id);
}
IPA_MHI_ERR("err_desc=0x%x\n", notify->err_desc);
+ ipa_assert();
}
static void ipa_mhi_gsi_ch_err_cb(struct gsi_chan_err_notify *notify)
@@ -1090,6 +1091,7 @@ static void ipa_mhi_gsi_ch_err_cb(struct gsi_chan_err_notify *notify)
IPA_MHI_ERR("Unexpected err evt: %d\n", notify->evt_id);
}
IPA_MHI_ERR("err_desc=0x%x\n", notify->err_desc);
+ ipa_assert();
}
@@ -2044,6 +2046,8 @@ static int ipa_mhi_suspend_dl(bool force)
if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI)
ipa_mhi_update_host_ch_state(true);
+ return 0;
+
fail_stop_event_update_dl_channel:
ipa_mhi_resume_channels(true,
ipa_mhi_client_ctx->dl_channels);
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_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 2214dfe89df3..f2909110d09f 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -857,12 +857,16 @@ int ipa2_query_rt_index(struct ipa_ioc_get_rt_tbl_indx *in)
return -EINVAL;
}
+ mutex_lock(&ipa_ctx->lock);
/* check if this table exists */
entry = __ipa_find_rt_tbl(in->ip, in->name);
- if (!entry)
+ if (!entry) {
+ mutex_unlock(&ipa_ctx->lock);
return -EFAULT;
+ }
in->idx = entry->idx;
+ mutex_unlock(&ipa_ctx->lock);
return 0;
}
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_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index ce899ef9c531..834712a71ac6 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -2618,6 +2618,9 @@ static int rmnet_ipa_set_data_quota_modem(struct wan_ioctl_set_data_quota *data)
if (!data->set_quota)
ipa_qmi_stop_data_qouta();
+ /* prevent string buffer overflows */
+ data->interface_name[IFNAMSIZ-1] = '\0';
+
index = find_vchannel_name_index(data->interface_name);
IPAWANERR("iface name %s, quota %lu\n",
data->interface_name,
@@ -2855,6 +2858,10 @@ int rmnet_ipa_query_tethering_stats_modem(
kfree(req);
kfree(resp);
return rc;
+ } else if (data == NULL) {
+ kfree(req);
+ kfree(resp);
+ return 0;
}
if (resp->dl_dst_pipe_stats_list_valid) {
@@ -3038,8 +3045,11 @@ int rmnet_ipa_query_tethering_stats_all(
int rmnet_ipa_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data)
{
enum ipa_upstream_type upstream_type;
+ struct wan_ioctl_query_tether_stats tether_stats;
int rc = 0;
+ memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->upstreamIface);
@@ -3057,7 +3067,7 @@ int rmnet_ipa_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data)
} else {
IPAWANDBG(" reset modem-backhaul stats\n");
rc = rmnet_ipa_query_tethering_stats_modem(
- NULL, true);
+ &tether_stats, true);
if (rc) {
IPAWANERR("reset MODEM stats failed\n");
return rc;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index e349ade46075..bc6622d4725b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -1668,8 +1668,21 @@ static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id,
if (should_force_clear) {
result = ipa3_enable_force_clear(qmi_req_id, false,
source_pipe_bitmask);
- if (result)
- goto exit;
+ if (result) {
+ struct ipahal_ep_cfg_ctrl_scnd ep_ctrl_scnd = { 0 };
+
+ /*
+ * assuming here modem SSR\shutdown, AP can remove
+ * the delay in this case
+ */
+ IPAERR(
+ "failed to force clear %d, remove delay from SCND reg\n"
+ , result);
+ ep_ctrl_scnd.endp_delay = false;
+ ipahal_write_reg_n_fields(
+ IPA_ENDP_INIT_CTRL_SCND_n, clnt_hdl,
+ &ep_ctrl_scnd);
+ }
}
/* with force clear, wait for emptiness */
for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 0492fa27c5b7..6a3a89e2cf8d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -356,7 +356,7 @@ int ipa3_send_one(struct ipa3_sys_context *sys, struct ipa3_desc *desc,
dma_address = desc->dma_address;
tx_pkt->no_unmap_dma = true;
}
- if (!dma_address) {
+ if (dma_mapping_error(ipa3_ctx->pdev, dma_address)) {
IPAERR("failed to DMA wrap\n");
goto fail_dma_map;
}
@@ -471,7 +471,6 @@ int ipa3_send(struct ipa3_sys_context *sys,
int i = 0;
int j;
int result;
- int fail_dma_wrap = 0;
uint size;
u32 mem_flag = GFP_ATOMIC;
int ipa_ep_idx;
@@ -527,7 +526,7 @@ int ipa3_send(struct ipa3_sys_context *sys,
}
dma_addr = dma_map_single(ipa3_ctx->pdev,
transfer.iovec, size, DMA_TO_DEVICE);
- if (!dma_addr) {
+ if (dma_mapping_error(ipa3_ctx->pdev, dma_addr)) {
IPAERR("dma_map_single failed\n");
kfree(transfer.iovec);
return -EFAULT;
@@ -540,7 +539,6 @@ int ipa3_send(struct ipa3_sys_context *sys,
spin_lock_bh(&sys->spinlock);
for (i = 0; i < num_desc; i++) {
- fail_dma_wrap = 0;
tx_pkt = kmem_cache_zalloc(ipa3_ctx->tx_pkt_wrapper_cache,
mem_flag);
if (!tx_pkt) {
@@ -563,7 +561,7 @@ int ipa3_send(struct ipa3_sys_context *sys,
if (ipa_populate_tag_field(&desc[i], tx_pkt,
&tag_pyld_ret)) {
IPAERR("Failed to populate tag field\n");
- goto failure;
+ goto failure_dma_map;
}
}
@@ -579,11 +577,6 @@ int ipa3_send(struct ipa3_sys_context *sys,
tx_pkt->mem.base,
tx_pkt->mem.size,
DMA_TO_DEVICE);
- if (!tx_pkt->mem.phys_base) {
- IPAERR("failed to do dma map.\n");
- fail_dma_wrap = 1;
- goto failure;
- }
} else {
tx_pkt->mem.phys_base =
desc[i].dma_address;
@@ -599,17 +592,17 @@ int ipa3_send(struct ipa3_sys_context *sys,
desc[i].frag,
0, tx_pkt->mem.size,
DMA_TO_DEVICE);
- if (!tx_pkt->mem.phys_base) {
- IPAERR("dma map failed\n");
- fail_dma_wrap = 1;
- goto failure;
- }
} else {
tx_pkt->mem.phys_base =
desc[i].dma_address;
tx_pkt->no_unmap_dma = true;
}
}
+ if (dma_mapping_error(ipa3_ctx->pdev, tx_pkt->mem.phys_base)) {
+ IPAERR("failed to do dma map.\n");
+ goto failure_dma_map;
+ }
+
tx_pkt->sys = sys;
tx_pkt->callback = desc[i].callback;
tx_pkt->user1 = desc[i].user1;
@@ -704,29 +697,31 @@ int ipa3_send(struct ipa3_sys_context *sys,
spin_unlock_bh(&sys->spinlock);
return 0;
+failure_dma_map:
+ kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt);
+
failure:
ipahal_destroy_imm_cmd(tag_pyld_ret);
tx_pkt = tx_pkt_first;
for (j = 0; j < i; j++) {
next_pkt = list_next_entry(tx_pkt, link);
list_del(&tx_pkt->link);
- if (desc[j].type != IPA_DATA_DESC_SKB_PAGED) {
- dma_unmap_single(ipa3_ctx->pdev, tx_pkt->mem.phys_base,
- tx_pkt->mem.size,
- DMA_TO_DEVICE);
- } else {
- dma_unmap_page(ipa3_ctx->pdev, tx_pkt->mem.phys_base,
- tx_pkt->mem.size,
- DMA_TO_DEVICE);
+
+ if (!tx_pkt->no_unmap_dma) {
+ if (desc[j].type != IPA_DATA_DESC_SKB_PAGED) {
+ dma_unmap_single(ipa3_ctx->pdev,
+ tx_pkt->mem.phys_base,
+ tx_pkt->mem.size, DMA_TO_DEVICE);
+ } else {
+ dma_unmap_page(ipa3_ctx->pdev,
+ tx_pkt->mem.phys_base,
+ tx_pkt->mem.size,
+ DMA_TO_DEVICE);
+ }
}
kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt);
tx_pkt = next_pkt;
}
- if (j < num_desc)
- /* last desc failed */
- if (fail_dma_wrap)
- kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt);
-
if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
kfree(gsi_xfer_elem_array);
} else {
@@ -1973,8 +1968,7 @@ begin:
rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev, ptr,
sys->rx_buff_sz,
DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa3_ctx->pdev, rx_pkt->data.dma_addr)) {
pr_err_ratelimited("%s dma map fail %p for %p sys=%p\n",
__func__, (void *)rx_pkt->data.dma_addr,
ptr, sys);
@@ -2141,8 +2135,7 @@ static void ipa3_alloc_wlan_rx_common_cache(u32 size)
ptr = skb_put(rx_pkt->data.skb, IPA_WLAN_RX_BUFF_SZ);
rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev, ptr,
IPA_WLAN_RX_BUFF_SZ, DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa3_ctx->pdev, rx_pkt->data.dma_addr)) {
IPAERR("dma_map_single failure %p for %p\n",
(void *)rx_pkt->data.dma_addr, ptr);
goto fail_dma_mapping;
@@ -2213,8 +2206,7 @@ static void ipa3_replenish_rx_cache(struct ipa3_sys_context *sys)
rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev, ptr,
sys->rx_buff_sz,
DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa3_ctx->pdev, rx_pkt->data.dma_addr)) {
IPAERR("dma_map_single failure %p for %p\n",
(void *)rx_pkt->data.dma_addr, ptr);
goto fail_dma_mapping;
@@ -2304,8 +2296,8 @@ static void ipa3_replenish_rx_cache_recycle(struct ipa3_sys_context *sys)
ptr = skb_put(rx_pkt->data.skb, sys->rx_buff_sz);
rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev,
ptr, sys->rx_buff_sz, DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa3_ctx->pdev,
+ rx_pkt->data.dma_addr)) {
IPAERR("dma_map_single failure %p for %p\n",
(void *)rx_pkt->data.dma_addr, ptr);
goto fail_dma_mapping;
@@ -2320,8 +2312,8 @@ static void ipa3_replenish_rx_cache_recycle(struct ipa3_sys_context *sys)
ptr = skb_put(rx_pkt->data.skb, sys->rx_buff_sz);
rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev,
ptr, sys->rx_buff_sz, DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa3_ctx->pdev,
+ rx_pkt->data.dma_addr)) {
IPAERR("dma_map_single failure %p for %p\n",
(void *)rx_pkt->data.dma_addr, ptr);
goto fail_dma_mapping;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index 69dca7666346..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,16 +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);
- } 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) {
@@ -513,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;
}
@@ -569,6 +574,9 @@ fail_add_proc_ctx:
list_del(&entry->link);
dma_unmap_single(ipa3_ctx->pdev, entry->phys_base,
entry->hdr_len, DMA_TO_DEVICE);
+fail_dma_mapping:
+ entry->is_hdr_proc_ctx = false;
+
bad_hdr_len:
entry->cookie = 0;
kmem_cache_free(ipa3_ctx->hdr_cache, entry);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index 9e2ffe70170c..cd027c28e597 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -255,6 +255,24 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client,
ep->gsi_evt_ring_hdl = *params->cached_gsi_evt_ring_hdl;
}
+ if (params->ev_ctx_host->wp == params->ev_ctx_host->rbase) {
+ IPA_MHI_ERR("event ring wp is not updated. base=wp=0x%llx\n",
+ params->ev_ctx_host->wp);
+ goto fail_alloc_ch;
+ return res;
+ }
+
+ IPA_MHI_DBG("Ring event db: evt_ring_hdl=%lu host_wp=0x%llx\n",
+ ep->gsi_evt_ring_hdl, params->ev_ctx_host->wp);
+ res = gsi_ring_evt_ring_db(ep->gsi_evt_ring_hdl,
+ params->ev_ctx_host->wp);
+ if (res) {
+ IPA_MHI_ERR("fail to ring evt ring db %d. hdl=%lu wp=0x%llx\n",
+ res, ep->gsi_evt_ring_hdl, params->ev_ctx_host->wp);
+ goto fail_alloc_ch;
+ return res;
+ }
+
memset(&ch_props, 0, sizeof(ch_props));
ch_props.prot = GSI_CHAN_PROT_MHI;
ch_props.dir = IPA_CLIENT_IS_PROD(client) ?
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index 7212ba2a165c..6197c9f64ca5 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -727,12 +727,15 @@ int ipa3_query_rt_index(struct ipa_ioc_get_rt_tbl_indx *in)
return -EINVAL;
}
+ mutex_lock(&ipa3_ctx->lock);
/* check if this table exists */
entry = __ipa3_find_rt_tbl(in->ip, in->name);
- if (!entry)
+ if (!entry) {
+ mutex_unlock(&ipa3_ctx->lock);
return -EFAULT;
-
+ }
in->idx = entry->idx;
+ mutex_unlock(&ipa3_ctx->lock);
return 0;
}
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/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 03dbcbb059aa..039bc7da5153 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1140,7 +1140,8 @@ send:
memset(&meta, 0, sizeof(meta));
meta.pkt_init_dst_ep_valid = true;
meta.pkt_init_dst_ep_remote = true;
- meta.pkt_init_dst_ep = IPA_CLIENT_Q6_LAN_CONS;
+ meta.pkt_init_dst_ep =
+ ipa3_get_ep_mapping(IPA_CLIENT_Q6_WAN_CONS);
ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, &meta);
} else {
ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, NULL);
@@ -2745,6 +2746,9 @@ static int rmnet_ipa3_set_data_quota_modem(
if (!data->set_quota)
ipa3_qmi_stop_data_qouta();
+ /* prevent string buffer overflows */
+ data->interface_name[IFNAMSIZ-1] = '\0';
+
index = find_vchannel_name_index(data->interface_name);
IPAWANERR("iface name %s, quota %lu\n",
data->interface_name,
@@ -2982,6 +2986,10 @@ static int rmnet_ipa3_query_tethering_stats_modem(
kfree(req);
kfree(resp);
return rc;
+ } else if (data == NULL) {
+ kfree(req);
+ kfree(resp);
+ return 0;
}
if (resp->dl_dst_pipe_stats_list_valid) {
@@ -3165,8 +3173,11 @@ int rmnet_ipa3_query_tethering_stats_all(
int rmnet_ipa3_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data)
{
enum ipa_upstream_type upstream_type;
+ struct wan_ioctl_query_tether_stats tether_stats;
int rc = 0;
+ memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->upstreamIface);
@@ -3184,7 +3195,7 @@ int rmnet_ipa3_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data)
} else {
IPAWANERR(" reset modem-backhaul stats\n");
rc = rmnet_ipa3_query_tethering_stats_modem(
- NULL, true);
+ &tether_stats, true);
if (rc) {
IPAWANERR("reset MODEM stats failed\n");
return rc;
diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
index 5a41d641de4f..3a89c7dffdb0 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -326,6 +326,7 @@ struct ipa_test_mhi_context {
struct ipa_mem_buffer out_buffer;
u32 prod_hdl;
u32 cons_hdl;
+ u32 test_prod_hdl;
};
static struct ipa_test_mhi_context *test_mhi_ctx;
@@ -774,6 +775,7 @@ fail_destroy_out_ch_ctx:
static int ipa_test_mhi_suite_setup(void **ppriv)
{
int rc = 0;
+ struct ipa_sys_connect_params sys_in;
IPA_UT_DBG("Start Setup\n");
@@ -815,9 +817,22 @@ static int ipa_test_mhi_suite_setup(void **ppriv)
goto fail_free_mmio_spc;
}
+ /* connect PROD pipe for remote wakeup */
+ memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
+ sys_in.client = IPA_CLIENT_TEST_PROD;
+ sys_in.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+ sys_in.ipa_ep_cfg.mode.mode = IPA_DMA;
+ sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_MHI_CONS;
+ if (ipa_setup_sys_pipe(&sys_in, &test_mhi_ctx->test_prod_hdl)) {
+ IPA_UT_ERR("setup sys pipe failed.\n");
+ goto fail_destroy_data_structures;
+ }
+
*ppriv = test_mhi_ctx;
return 0;
+fail_destroy_data_structures:
+ ipa_mhi_test_destroy_data_structures();
fail_free_mmio_spc:
ipa_test_mhi_free_mmio_space();
fail_iounmap:
@@ -838,6 +853,7 @@ static int ipa_test_mhi_suite_teardown(void *priv)
if (!test_mhi_ctx)
return 0;
+ ipa_teardown_sys_pipe(test_mhi_ctx->test_prod_hdl);
ipa_mhi_test_destroy_data_structures();
ipa_test_mhi_free_mmio_space();
iounmap(test_mhi_ctx->gsi_mmio);
@@ -1811,7 +1827,7 @@ static int ipa_mhi_test_create_aggr_open_frame(void)
memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1);
}
- rc = ipa_tx_dp(IPA_CLIENT_MHI_CONS, skb, NULL);
+ rc = ipa_tx_dp(IPA_CLIENT_TEST_PROD, skb, NULL);
if (rc) {
IPA_UT_LOG("ipa_tx_dp failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("ipa tx dp fail");
@@ -1982,7 +1998,7 @@ static int ipa_mhi_test_suspend_host_wakeup(void)
memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1);
}
- rc = ipa_tx_dp(IPA_CLIENT_MHI_CONS, skb, NULL);
+ rc = ipa_tx_dp(IPA_CLIENT_TEST_PROD, skb, NULL);
if (rc) {
IPA_UT_LOG("ipa_tx_dp failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("ipa tx dp fail");
diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c
index e1c50e1273ac..68ef2595f3c3 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.c
+++ b/drivers/platform/msm/mhi/mhi_bhi.c
@@ -249,6 +249,13 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic)
{
struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
struct bhie_vec_table *rddm_table = &bhi_ctxt->rddm_table;
+ struct bhie_mem_info *bhie_mem_info;
+ u32 rx_sequence, val, current_seq;
+ u32 timeout = (bhi_ctxt->poll_timeout * 1000) / BHIE_RDDM_DELAY_TIME_US;
+ int i;
+ u32 cur_exec, prev_exec = 0;
+ u32 state, prev_state = 0;
+ u32 rx_status, prev_status = 0;
if (!rddm_table->bhie_mem_info) {
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "RDDM table == NULL\n");
@@ -258,9 +265,93 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic)
if (!in_panic)
return bhi_rddm_graceful(mhi_dev_ctxt);
- mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
- "RDDM collection in panic not yet supported\n");
- return -EINVAL;
+ /*
+ * Below code should only be executed during kernel panic,
+ * we expect other cores to be shutting down while we're
+ * executing rddm transfer. After returning from this function,
+ * we expect device to reset.
+ */
+
+ /* Trigger device into RDDM */
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "pm_state:0x%x mhi_state:%s\n",
+ mhi_dev_ctxt->mhi_pm_state,
+ TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Register access not allowed\n");
+ return -EIO;
+ }
+
+ /*
+ * Normally we only set mhi_pm_state after grabbing pm_xfer_lock as a
+ * write, by function mhi_tryset_pm_state. Since we're in a kernel
+ * panic, we will set pm state w/o grabbing xfer lock. We're setting
+ * pm_state to LD as a safety precautions. If another core in middle
+ * of register access this should deter it. However, there is no
+ * no gurantee change will take effect.
+ */
+ mhi_dev_ctxt->mhi_pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
+ /* change should take effect immediately */
+ smp_wmb();
+
+ bhie_mem_info = &rddm_table->
+ bhie_mem_info[rddm_table->segment_count - 1];
+ rx_sequence = rddm_table->sequence++;
+
+ /* program the vector table */
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Programming RXVEC table\n");
+ val = HIGH_WORD(bhie_mem_info->phys_addr);
+ mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base,
+ BHIE_RXVECADDR_HIGH_OFFS, val);
+ val = LOW_WORD(bhie_mem_info->phys_addr);
+ mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECADDR_LOW_OFFS,
+ val);
+ val = (u32)bhie_mem_info->size;
+ mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECSIZE_OFFS,
+ val);
+ mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECDB_OFFS,
+ BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
+ rx_sequence);
+
+ /* trigger device into rddm */
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Triggering Device into RDDM mode\n");
+ mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_SYS_ERR);
+ i = 0;
+
+ while (timeout--) {
+ cur_exec = mhi_reg_read(bhi_ctxt->bhi_base, BHI_EXECENV);
+ state = mhi_get_m_state(mhi_dev_ctxt);
+ rx_status = mhi_reg_read(bhi_ctxt->bhi_base,
+ BHIE_RXVECSTATUS_OFFS);
+ /* if reg. values changed or each sec (udelay(1000)) log it */
+ if (cur_exec != prev_exec || state != prev_state ||
+ rx_status != prev_status || !(i & (SZ_1K - 1))) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "EXECENV:0x%x MHISTATE:0x%x RXSTATUS:0x%x\n",
+ cur_exec, state, rx_status);
+ prev_exec = cur_exec;
+ prev_state = state;
+ prev_status = rx_status;
+ };
+ current_seq = (rx_status & BHIE_TXVECSTATUS_SEQNUM_BMSK) >>
+ BHIE_TXVECSTATUS_SEQNUM_SHFT;
+ rx_status = (rx_status & BHIE_TXVECSTATUS_STATUS_BMSK) >>
+ BHIE_TXVECSTATUS_STATUS_SHFT;
+
+ if ((rx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) &&
+ (current_seq == rx_sequence)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "rddm transfer completed\n");
+ return 0;
+ }
+ udelay(BHIE_RDDM_DELAY_TIME_US);
+ i++;
+ }
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "rddm transfer timeout\n");
+
+ return -EIO;
}
static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -439,7 +530,6 @@ void bhi_firmware_download(struct work_struct *work)
struct bhi_ctxt_t *bhi_ctxt;
struct bhie_mem_info mem_info;
int ret;
- long timeout;
mhi_dev_ctxt = container_of(work, struct mhi_device_ctxt,
bhi_ctxt.fw_load_work);
@@ -447,8 +537,16 @@ 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,
- mhi_dev_ctxt->mhi_state == MHI_STATE_BHI);
+ 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,
+ 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;
+ }
/* PBL image is the first segment in firmware vector table */
mem_info = *bhi_ctxt->fw_table.bhie_mem_info;
@@ -462,10 +560,12 @@ void bhi_firmware_download(struct work_struct *work)
mhi_init_state_transition(mhi_dev_ctxt,
STATE_TRANSITION_RESET);
- timeout = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
- mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE,
- msecs_to_jiffies(bhi_ctxt->poll_timeout));
- if (!timeout) {
+ wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE ||
+ mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
+ msecs_to_jiffies(bhi_ctxt->poll_timeout));
+ if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT ||
+ mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_BHIE) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to Enter EXEC_ENV_BHIE\n");
return;
diff --git a/drivers/platform/msm/mhi/mhi_bhi.h b/drivers/platform/msm/mhi/mhi_bhi.h
index 8f7b3d69347c..8f9bc52bbbe0 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.h
+++ b/drivers/platform/msm/mhi/mhi_bhi.h
@@ -87,6 +87,7 @@
#define BHI_POLL_SLEEP_TIME_MS 100
#define BHI_POLL_TIMEOUT_MS 2000
+#define BHIE_RDDM_DELAY_TIME_US (1000)
int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt);
void bhi_firmware_download(struct work_struct *work);
diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c
index 64a09a2f9fbb..a5936ea5a6aa 100644
--- a/drivers/platform/msm/mhi/mhi_iface.c
+++ b/drivers/platform/msm/mhi/mhi_iface.c
@@ -153,16 +153,19 @@ static int mhi_pci_probe(struct pci_dev *pcie_device,
u32 slot = PCI_SLOT(pcie_device->devfn);
unsigned long msi_requested, msi_required;
struct msm_pcie_register_event *mhi_pci_link_event;
+ struct pcie_core_info *core;
+ int i;
+ char node[32];
/* Find correct device context based on bdf & dev_id */
mutex_lock(&mhi_device_drv->lock);
list_for_each_entry(itr, &mhi_device_drv->head, node) {
- struct pcie_core_info *core = &itr->core;
-
- if (core->domain == domain &&
- core->bus == bus &&
- core->dev_id == dev_id &&
+ core = &itr->core;
+ if (core->domain == domain && core->bus == bus &&
+ (core->dev_id == PCI_ANY_ID || (core->dev_id == dev_id)) &&
core->slot == slot) {
+ /* change default dev_id to actual dev_id */
+ core->dev_id = dev_id;
mhi_dev_ctxt = itr;
break;
}
@@ -171,6 +174,11 @@ static int mhi_pci_probe(struct pci_dev *pcie_device,
if (!mhi_dev_ctxt)
return -EPROBE_DEFER;
+ snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u",
+ core->dev_id, core->domain, core->bus, core->slot);
+ mhi_dev_ctxt->mhi_ipc_log =
+ ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0);
+
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Processing Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n",
domain, bus, dev_id, slot);
@@ -280,9 +288,22 @@ static int mhi_pci_probe(struct pci_dev *pcie_device,
mutex_lock(&mhi_dev_ctxt->pm_lock);
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->mhi_pm_state = MHI_PM_POR;
- ret_val = set_mhi_base_state(mhi_dev_ctxt);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ /* notify all registered clients we probed */
+ for (i = 0; i < MHI_MAX_CHANNELS; i++) {
+ struct mhi_client_handle *client_handle =
+ mhi_dev_ctxt->client_handle_list[i];
+
+ if (!client_handle)
+ continue;
+ client_handle->dev_id = core->dev_id;
+ mhi_notify_client(client_handle, MHI_CB_MHI_PROBED);
+ }
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ ret_val = set_mhi_base_state(mhi_dev_ctxt);
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+
if (ret_val) {
mhi_log(mhi_dev_ctxt,
MHI_MSG_ERROR,
@@ -344,7 +365,6 @@ static int mhi_plat_probe(struct platform_device *pdev)
int r = 0, len;
struct mhi_device_ctxt *mhi_dev_ctxt;
struct pcie_core_info *core;
- char node[32];
struct device_node *of_node = pdev->dev.of_node;
u64 address_window[2];
@@ -377,7 +397,7 @@ static int mhi_plat_probe(struct platform_device *pdev)
core = &mhi_dev_ctxt->core;
r = of_property_read_u32(of_node, "qcom,pci-dev_id", &core->dev_id);
if (r)
- return r;
+ core->dev_id = PCI_ANY_ID;
r = of_property_read_u32(of_node, "qcom,pci-slot", &core->slot);
if (r)
@@ -391,14 +411,6 @@ static int mhi_plat_probe(struct platform_device *pdev)
if (r)
return r;
- snprintf(node, sizeof(node),
- "mhi_%04x_%02u.%02u.%02u",
- core->dev_id, core->domain, core->bus, core->slot);
- mhi_dev_ctxt->mhi_ipc_log =
- ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0);
- if (!mhi_dev_ctxt->mhi_ipc_log)
- pr_err("%s: Error creating ipc_log buffer\n", __func__);
-
r = of_property_read_u32(of_node, "qcom,mhi-ready-timeout",
&mhi_dev_ctxt->poll_reset_timeout_ms);
if (r)
@@ -407,10 +419,6 @@ static int mhi_plat_probe(struct platform_device *pdev)
mhi_dev_ctxt->dev_space.start_win_addr = address_window[0];
mhi_dev_ctxt->dev_space.end_win_addr = address_window[1];
- mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
- "Start Addr:0x%llx End_Addr:0x%llx\n",
- mhi_dev_ctxt->dev_space.start_win_addr,
- mhi_dev_ctxt->dev_space.end_win_addr);
r = of_property_read_u32(of_node, "qcom,bhi-alignment",
&mhi_dev_ctxt->bhi_ctxt.alignment);
@@ -471,7 +479,6 @@ static int mhi_plat_probe(struct platform_device *pdev)
mutex_lock(&mhi_device_drv->lock);
list_add_tail(&mhi_dev_ctxt->node, &mhi_device_drv->head);
mutex_unlock(&mhi_device_drv->lock);
- mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
return 0;
}
diff --git a/drivers/platform/msm/mhi/mhi_macros.h b/drivers/platform/msm/mhi/mhi_macros.h
index fc0e6f4bc27d..ee0b9b759e0a 100644
--- a/drivers/platform/msm/mhi/mhi_macros.h
+++ b/drivers/platform/msm/mhi/mhi_macros.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -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
@@ -45,7 +44,7 @@
#define MHI_PCIE_DEVICE_ID_ZIRC 0x0301
#define MHI_PCIE_DEVICE_ID_9x55 0x0302
-#define MHI_M2_DEBOUNCE_TMR_MS 10
+#define MHI_M2_DEBOUNCE_TMR_US 10000
#define MHI_EV_DB_INTERVAL 1
diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c
index 702f9ad5ddfb..739915c4af6f 100644
--- a/drivers/platform/msm/mhi/mhi_main.c
+++ b/drivers/platform/msm/mhi/mhi_main.c
@@ -562,6 +562,7 @@ int mhi_register_channel(struct mhi_client_handle **client_handle,
(*client_handle)->domain = mhi_dev_ctxt->core.domain;
(*client_handle)->bus = mhi_dev_ctxt->core.bus;
(*client_handle)->slot = mhi_dev_ctxt->core.slot;
+ (*client_handle)->enabled = false;
client_config = (*client_handle)->client_config;
client_config->mhi_dev_ctxt = mhi_dev_ctxt;
client_config->user_data = client_info->user_data;
@@ -588,11 +589,8 @@ int mhi_register_channel(struct mhi_client_handle **client_handle,
}
if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS &&
- mhi_dev_ctxt->flags.mhi_initialized) {
- mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
- "Exec env is AMSS notify client now chan:%u\n", chan);
- mhi_notify_client(*client_handle, MHI_CB_MHI_ENABLED);
- }
+ mhi_dev_ctxt->flags.mhi_initialized)
+ (*client_handle)->enabled = true;
mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Successfuly registered chan:%u\n", chan);
@@ -1789,6 +1787,8 @@ int mhi_register_device(struct mhi_device *mhi_device,
u32 dev_id = pci_dev->device;
u32 slot = PCI_SLOT(pci_dev->devfn);
int ret, i;
+ char node[32];
+ struct pcie_core_info *core;
of_node = of_parse_phandle(mhi_device->dev->of_node, node_name, 0);
if (!of_node)
@@ -1801,13 +1801,13 @@ int mhi_register_device(struct mhi_device *mhi_device,
mutex_lock(&mhi_device_drv->lock);
list_for_each_entry(itr, &mhi_device_drv->head, node) {
struct platform_device *pdev = itr->plat_dev;
- struct pcie_core_info *core = &itr->core;
- if (pdev->dev.of_node == of_node &&
- core->domain == domain &&
- core->bus == bus &&
- core->dev_id == dev_id &&
- core->slot == slot) {
+ core = &itr->core;
+ if (pdev->dev.of_node == of_node && core->domain == domain &&
+ core->bus == bus && core->slot == slot &&
+ (core->dev_id == PCI_ANY_ID || (core->dev_id == dev_id))) {
+ /* change default dev_id to current dev_id */
+ core->dev_id = dev_id;
mhi_dev_ctxt = itr;
break;
}
@@ -1818,6 +1818,11 @@ int mhi_register_device(struct mhi_device *mhi_device,
if (!mhi_dev_ctxt)
return -EPROBE_DEFER;
+ snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u",
+ core->dev_id, core->domain, core->bus, core->slot);
+ mhi_dev_ctxt->mhi_ipc_log =
+ ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0);
+
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Registering Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n",
domain, bus, dev_id, slot);
@@ -1899,6 +1904,17 @@ int mhi_register_device(struct mhi_device *mhi_device,
mhi_dev_ctxt->bhi_ctxt.rddm_size);
}
+ /* notify all the registered clients we probed */
+ for (i = 0; i < MHI_MAX_CHANNELS; i++) {
+ struct mhi_client_handle *client_handle =
+ mhi_dev_ctxt->client_handle_list[i];
+
+ if (!client_handle)
+ continue;
+ client_handle->dev_id = core->dev_id;
+ mhi_notify_client(client_handle, MHI_CB_MHI_PROBED);
+ }
+
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit success\n");
return 0;
}
diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c
index caa34eadf8ea..49db99100311 100644
--- a/drivers/platform/msm/mhi/mhi_pm.c
+++ b/drivers/platform/msm/mhi/mhi_pm.c
@@ -22,6 +22,22 @@
#include "mhi_hwio.h"
#include "mhi_bhi.h"
+static const char *const mhi_dev_ctrl_str[MHI_DEV_CTRL_MAXCMD] = {
+ [MHI_DEV_CTRL_INIT] = "INIT",
+ [MHI_DEV_CTRL_DE_INIT] = "DE-INIT",
+ [MHI_DEV_CTRL_SUSPEND] = "SUSPEND",
+ [MHI_DEV_CTRL_RESUME] = "RESUME",
+ [MHI_DEV_CTRL_POWER_OFF] = "OFF",
+ [MHI_DEV_CTRL_POWER_ON] = "ON",
+ [MHI_DEV_CTRL_TRIGGER_RDDM] = "TRIGGER RDDM",
+ [MHI_DEV_CTRL_RDDM] = "RDDM",
+ [MHI_DEV_CTRL_RDDM_KERNEL_PANIC] = "RDDM IN PANIC",
+ [MHI_DEV_CTRL_NOTIFY_LINK_ERROR] = "LD",
+};
+
+#define TO_MHI_DEV_CTRL_STR(cmd) ((cmd >= MHI_DEV_CTRL_MAXCMD) ? "INVALID" : \
+ mhi_dev_ctrl_str[cmd])
+
/* Write only sysfs attributes */
static DEVICE_ATTR(MHI_M0, S_IWUSR, NULL, sysfs_init_m0);
static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3);
@@ -97,12 +113,14 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
- mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
- mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
- if (!r) {
+ 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_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, timeout, current state:%s\n",
+ "Failed to get M0||M1 event or LD pm_state:0x%x state:%s\n",
+ mhi_dev_ctxt->mhi_pm_state,
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
return -EIO;
}
@@ -122,9 +140,10 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Waiting for M3 completion.\n");
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event,
- mhi_dev_ctxt->mhi_state == MHI_STATE_M3,
- msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT));
- if (!r) {
+ 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_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",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
@@ -158,12 +177,13 @@ static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0);
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
- mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
- mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
- if (!r) {
+ 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_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\n");
+ "Failed to get M0 event, timeout or LD\n");
r = -EIO;
} else
r = 0;
@@ -295,9 +315,9 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- ret_val = wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete,
- msecs_to_jiffies(timeout));
- if (!ret_val || mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS)
+ wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete,
+ msecs_to_jiffies(timeout));
+ if (mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS)
ret_val = -EIO;
else
ret_val = 0;
@@ -310,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;
@@ -537,16 +560,16 @@ void mhi_link_state_cb(struct msm_pcie_notify *notify)
}
}
-int mhi_pm_control_device(struct mhi_device *mhi_device,
- enum mhi_dev_ctrl ctrl)
+int mhi_pm_control_device(struct mhi_device *mhi_device, enum mhi_dev_ctrl ctrl)
{
struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->mhi_dev_ctxt;
+ unsigned long flags;
if (!mhi_dev_ctxt)
return -EINVAL;
- mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
- "Entered with cmd:%d\n", ctrl);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered with cmd:%s\n",
+ TO_MHI_DEV_CTRL_STR(ctrl));
switch (ctrl) {
case MHI_DEV_CTRL_INIT:
@@ -560,12 +583,46 @@ int mhi_pm_control_device(struct mhi_device *mhi_device,
case MHI_DEV_CTRL_POWER_OFF:
mhi_pm_slave_mode_power_off(mhi_dev_ctxt);
break;
+ case MHI_DEV_CTRL_TRIGGER_RDDM:
+ write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock,
+ flags);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "failed to trigger rddm, no register access in state:0x%x\n",
+ mhi_dev_ctxt->mhi_pm_state);
+ return -EIO;
+ }
+ mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_SYS_ERR);
+ write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
+ break;
case MHI_DEV_CTRL_RDDM:
return bhi_rddm(mhi_dev_ctxt, false);
+ case MHI_DEV_CTRL_RDDM_KERNEL_PANIC:
+ return bhi_rddm(mhi_dev_ctxt, true);
case MHI_DEV_CTRL_DE_INIT:
- if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE)
+ if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE) {
+ enum MHI_PM_STATE cur_state;
+ /*
+ * If bus master calls DE_INIT before calling POWER_OFF
+ * means a critical failure occurred during POWER_ON
+ * state transition and external PCIe device may not
+ * respond to host. Force PM state to PCIe linkdown
+ * state prior to starting shutdown process to avoid
+ * accessing PCIe link.
+ */
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt,
+ MHI_PM_LD_ERR_FATAL_DETECT);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ if (unlikely(cur_state != MHI_PM_LD_ERR_FATAL_DETECT)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ MHI_PM_LD_ERR_FATAL_DETECT, cur_state);
+ }
process_disable_transition(MHI_PM_SHUTDOWN_PROCESS,
mhi_dev_ctxt);
+ }
bhi_exit(mhi_dev_ctxt);
break;
case MHI_DEV_CTRL_NOTIFY_LINK_ERROR:
@@ -580,6 +637,12 @@ int mhi_pm_control_device(struct mhi_device *mhi_device,
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Failed to transition to state 0x%x from 0x%x\n",
MHI_PM_LD_ERR_FATAL_DETECT, cur_state);
+
+ /* wake up all threads that's waiting for state change events */
+ complete(&mhi_dev_ctxt->cmd_complete);
+ wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
+ wake_up(mhi_dev_ctxt->mhi_ev_wq.m0_event);
+ wake_up(mhi_dev_ctxt->mhi_ev_wq.m3_event);
break;
}
default:
diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c
index c0c23c4e0756..ea2a91bd2d06 100644
--- a/drivers/platform/msm/mhi/mhi_states.c
+++ b/drivers/platform/msm/mhi/mhi_states.c
@@ -147,7 +147,8 @@ void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt,
* M1 -> M3_ENTER --> M3
* L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR
* L2: SHUTDOWN_PROCESS -> DISABLE -> SSR_PENDING (via SSR Notification only)
- * L3: LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS
+ * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT
+ * LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS
*/
static const struct mhi_pm_transitions const mhi_state_transitions[] = {
/* L0 States */
@@ -216,7 +217,7 @@ static const struct mhi_pm_transitions const mhi_state_transitions[] = {
/* L3 States */
{
MHI_PM_LD_ERR_FATAL_DETECT,
- MHI_PM_SHUTDOWN_PROCESS
+ MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_PROCESS
},
/* From SSR notification only */
{
@@ -430,7 +431,7 @@ void process_m1_transition(struct work_struct *work)
mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M2);
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- msleep(MHI_M2_DEBOUNCE_TMR_MS);
+ usleep_range(MHI_M2_DEBOUNCE_TMR_US, MHI_M2_DEBOUNCE_TMR_US + 50);
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
/* During DEBOUNCE Time We could be receiving M0 Event */
@@ -590,26 +591,26 @@ static int process_reset_transition(
}
static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
- enum MHI_EXEC_ENV exec_env)
+ enum MHI_EXEC_ENV exec_env)
{
struct mhi_client_handle *client_handle = NULL;
- struct mhi_cb_info cb_info;
- int i = 0, r = 0;
- struct mhi_chan_info chan_info;
-
- cb_info.cb_reason = MHI_CB_MHI_ENABLED;
+ struct mhi_chan_info *chan_info;
+ int i = 0;
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Enabling Clients, exec env %d.\n", exec_env);
+
for (i = 0; i < MHI_MAX_CHANNELS; ++i) {
- if (!VALID_CHAN_NR(i))
+ if (!mhi_dev_ctxt->client_handle_list[i])
continue;
+
client_handle = mhi_dev_ctxt->client_handle_list[i];
- r = get_chan_props(mhi_dev_ctxt, i, &chan_info);
- if (!r && client_handle &&
- exec_env == GET_CHAN_PROPS(CHAN_BRINGUP_STAGE,
- chan_info.flags))
+ chan_info = &client_handle->client_config->chan_info;
+ if (exec_env == GET_CHAN_PROPS(CHAN_BRINGUP_STAGE,
+ chan_info->flags)) {
+ client_handle->enabled = true;
mhi_notify_client(client_handle, MHI_CB_MHI_ENABLED);
+ }
}
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done.\n");
diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c
index 3191ec065a95..4563810d7e5b 100644
--- a/drivers/platform/msm/mhi_uci/mhi_uci.c
+++ b/drivers/platform/msm/mhi_uci/mhi_uci.c
@@ -122,6 +122,7 @@ struct uci_client {
atomic_t out_pkt_pend_ack;
atomic_t completion_ack;
struct mhi_uci_ctxt_t *uci_ctxt;
+ struct cdev cdev;
bool enabled;
void *uci_ipc_log;
};
@@ -132,7 +133,6 @@ struct mhi_uci_ctxt_t {
struct uci_client client_handles[MHI_SOFTWARE_CLIENT_LIMIT];
dev_t dev_t;
struct mutex ctrl_mutex;
- struct cdev cdev[MHI_SOFTWARE_CLIENT_LIMIT];
struct uci_client *ctrl_client;
};
@@ -246,6 +246,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait);
static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
+static int mhi_uci_create_device(struct uci_client *uci_client);
static struct mhi_uci_drv_ctxt mhi_uci_drv_ctxt;
@@ -1174,6 +1175,14 @@ static void uci_xfer_cb(struct mhi_cb_info *cb_info)
uci_handle = cb_info->result->user_data;
switch (cb_info->cb_reason) {
+ case MHI_CB_MHI_PROBED:
+ /* If it's outbound channel create the node */
+ mutex_lock(&uci_handle->client_lock);
+ if (!uci_handle->dev &&
+ cb_info->chan == uci_handle->out_attr.chan_id)
+ mhi_uci_create_device(uci_handle);
+ mutex_unlock(&uci_handle->client_lock);
+ break;
case MHI_CB_MHI_ENABLED:
uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
"MHI enabled CB received for chan %d\n",
@@ -1295,17 +1304,65 @@ static const struct file_operations mhi_uci_client_fops = {
.unlocked_ioctl = mhi_uci_ctl_ioctl,
};
+static int mhi_uci_create_device(struct uci_client *uci_client)
+{
+ struct mhi_uci_ctxt_t *uci_ctxt = uci_client->uci_ctxt;
+ char node_name[32];
+ int index = uci_client - uci_ctxt->client_handles;
+ int ret;
+
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO,
+ "Creating dev node %04x_%02u.%02u.%02u_pipe%d\n",
+ uci_client->out_attr.mhi_handle->dev_id,
+ uci_client->out_attr.mhi_handle->domain,
+ uci_client->out_attr.mhi_handle->bus,
+ uci_client->out_attr.mhi_handle->slot,
+ uci_client->out_attr.chan_id);
+
+ cdev_init(&uci_client->cdev, &mhi_uci_client_fops);
+ uci_client->cdev.owner = THIS_MODULE;
+ ret = cdev_add(&uci_client->cdev, uci_ctxt->dev_t + index, 1);
+ if (ret) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR,
+ "Failed to add cdev %d, ret:%d\n", index, ret);
+ return ret;
+ }
+ uci_client->dev = device_create(mhi_uci_drv_ctxt.mhi_uci_class, NULL,
+ uci_ctxt->dev_t + index, NULL,
+ DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d",
+ uci_client->out_attr.mhi_handle->dev_id,
+ uci_client->out_attr.mhi_handle->domain,
+ uci_client->out_attr.mhi_handle->bus,
+ uci_client->out_attr.mhi_handle->slot,
+ "_pipe_",
+ uci_client->out_attr.chan_id);
+ if (IS_ERR(uci_client->dev)) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR,
+ "Failed to add cdev %d\n", IS_ERR(uci_client->dev));
+ cdev_del(&uci_client->cdev);
+ return -EIO;
+ }
+
+ /* dev node created successfully, create logging buffer */
+ snprintf(node_name, sizeof(node_name), "mhi_uci_%04x_%02u.%02u.%02u_%d",
+ uci_client->out_attr.mhi_handle->dev_id,
+ uci_client->out_attr.mhi_handle->domain,
+ uci_client->out_attr.mhi_handle->bus,
+ uci_client->out_attr.mhi_handle->slot,
+ uci_client->out_attr.chan_id);
+ uci_client->uci_ipc_log = ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES,
+ node_name, 0);
+
+ return 0;
+}
+
static int mhi_uci_probe(struct platform_device *pdev)
{
struct mhi_uci_ctxt_t *uci_ctxt;
int ret_val;
int i;
- char node_name[32];
- uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
- UCI_DBG_INFO,
- "Entered with pdev:%p\n",
- pdev);
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO, "Entered\n");
if (mhi_is_device_ready(&pdev->dev, "qcom,mhi") == false)
return -EPROBE_DEFER;
@@ -1326,128 +1383,67 @@ static int mhi_uci_probe(struct platform_device *pdev)
uci_ctxt->pdev = pdev;
mutex_init(&uci_ctxt->ctrl_mutex);
- uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
- UCI_DBG_INFO,
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO,
"Setting up channel attributes\n");
ret_val = uci_init_client_attributes(uci_ctxt,
pdev->dev.of_node);
if (ret_val) {
- uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
- UCI_DBG_ERROR,
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR,
"Failed to init client attributes\n");
return -EIO;
}
- uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
- UCI_DBG_INFO,
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO,
+ "Allocating char devices\n");
+ ret_val = alloc_chrdev_region(&uci_ctxt->dev_t, 0,
+ MHI_SOFTWARE_CLIENT_LIMIT,
+ DEVICE_NAME);
+ if (IS_ERR_VALUE(ret_val)) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR,
+ "Failed to alloc char devs, ret 0x%x\n", ret_val);
+ return ret_val;
+ }
+
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO,
"Registering for MHI events\n");
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) {
struct uci_client *uci_client = &uci_ctxt->client_handles[i];
uci_client->uci_ctxt = uci_ctxt;
mutex_init(&uci_client->client_lock);
- if (uci_client->in_attr.uci_ownership) {
- ret_val = mhi_register_client(uci_client,
- &pdev->dev);
+ if (!uci_client->in_attr.uci_ownership)
+ continue;
+ ret_val = mhi_register_client(uci_client, &pdev->dev);
+ if (ret_val) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_CRITICAL,
+ "Failed to reg client %d ret %d\n",
+ ret_val, i);
+ return -EIO;
+ }
+
+ mutex_lock(&uci_client->client_lock);
+ /* If we have device id, create the node now */
+ if (uci_client->out_attr.mhi_handle->dev_id != PCI_ANY_ID) {
+ ret_val = mhi_uci_create_device(uci_client);
if (ret_val) {
uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
UCI_DBG_CRITICAL,
- "Failed to reg client %d ret %d\n",
- ret_val,
- i);
-
+ "Failed to create device node, ret:%d\n",
+ ret_val);
+ mutex_unlock(&uci_client->client_lock);
return -EIO;
}
- snprintf(node_name,
- sizeof(node_name),
- "mhi_uci_%04x_%02u.%02u.%02u_%d",
- uci_client->out_attr.mhi_handle->dev_id,
- uci_client->out_attr.mhi_handle->domain,
- uci_client->out_attr.mhi_handle->bus,
- uci_client->out_attr.mhi_handle->slot,
- uci_client->out_attr.chan_id);
- uci_client->uci_ipc_log = ipc_log_context_create
- (MHI_UCI_IPC_LOG_PAGES,
- node_name,
- 0);
}
+ mutex_unlock(&uci_client->client_lock);
}
- uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
- UCI_DBG_INFO,
- "Allocating char devices\n");
- ret_val = alloc_chrdev_region(&uci_ctxt->dev_t,
- 0,
- MHI_SOFTWARE_CLIENT_LIMIT,
- DEVICE_NAME);
- if (IS_ERR_VALUE(ret_val)) {
- uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
- UCI_DBG_ERROR,
- "Failed to alloc char devs, ret 0x%x\n", ret_val);
- goto failed_char_alloc;
- }
-
- uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
- UCI_DBG_INFO,
- "Setting up device nodes. for dev_t: 0x%x major:0x%x\n",
- uci_ctxt->dev_t,
- MAJOR(uci_ctxt->dev_t));
- for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) {
- struct uci_client *uci_client = &uci_ctxt->client_handles[i];
-
- if (uci_client->in_attr.uci_ownership) {
- cdev_init(&uci_ctxt->cdev[i], &mhi_uci_client_fops);
- uci_ctxt->cdev[i].owner = THIS_MODULE;
- ret_val = cdev_add(&uci_ctxt->cdev[i],
- uci_ctxt->dev_t + i, 1);
- if (IS_ERR_VALUE(ret_val)) {
- uci_log(uci_client->uci_ipc_log,
- UCI_DBG_ERROR,
- "Failed to add cdev %d, ret 0x%x\n",
- i, ret_val);
- goto failed_char_add;
- }
- uci_client->dev =
- device_create(mhi_uci_drv_ctxt.mhi_uci_class,
- NULL,
- uci_ctxt->dev_t + i,
- NULL,
- DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d",
- uci_client->out_attr.mhi_handle->dev_id,
- uci_client->out_attr.mhi_handle->domain,
- uci_client->out_attr.mhi_handle->bus,
- uci_client->out_attr.mhi_handle->slot,
- "_pipe_",
- uci_client->out_attr.chan_id);
- if (IS_ERR(uci_client->dev)) {
- uci_log(uci_client->uci_ipc_log,
- UCI_DBG_ERROR,
- "Failed to add cdev %d\n", i);
- cdev_del(&uci_ctxt->cdev[i]);
- ret_val = -EIO;
- goto failed_device_create;
- }
- }
- }
platform_set_drvdata(pdev, uci_ctxt);
mutex_lock(&mhi_uci_drv_ctxt.list_lock);
list_add_tail(&uci_ctxt->node, &mhi_uci_drv_ctxt.head);
mutex_unlock(&mhi_uci_drv_ctxt.list_lock);
- return 0;
-
-failed_char_add:
-failed_device_create:
- while (--i >= 0) {
- cdev_del(&uci_ctxt->cdev[i]);
- device_destroy(mhi_uci_drv_ctxt.mhi_uci_class,
- MKDEV(MAJOR(uci_ctxt->dev_t), i));
- };
-
- unregister_chrdev_region(MAJOR(uci_ctxt->dev_t),
- MHI_SOFTWARE_CLIENT_LIMIT);
-failed_char_alloc:
- return ret_val;
+ return 0;
};
static int mhi_uci_remove(struct platform_device *pdev)
@@ -1462,7 +1458,7 @@ static int mhi_uci_remove(struct platform_device *pdev)
if (uci_client->in_attr.uci_ownership) {
mhi_deregister_channel(uci_client->out_attr.mhi_handle);
mhi_deregister_channel(uci_client->in_attr.mhi_handle);
- cdev_del(&uci_ctxt->cdev[i]);
+ cdev_del(&uci_client->cdev);
device_destroy(mhi_uci_drv_ctxt.mhi_uci_class,
MKDEV(MAJOR(uci_ctxt->dev_t), i));
}
diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index 53c85773b377..4f7d27db8dee 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -221,6 +221,11 @@ static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp,
goto end;
}
+ if (state == EXT_DISPLAY_CABLE_CONNECT)
+ ext_disp->current_disp = type;
+ else
+ ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
+
ret = msm_ext_disp_send_cable_notification(ext_disp, state);
/* positive ret value means audio node was switched */
@@ -287,7 +292,6 @@ static bool msm_ext_disp_validate_connect(struct msm_ext_disp *ext_disp,
if (ext_disp->current_disp != type)
return false;
end:
- ext_disp->current_disp = type;
return true;
}
@@ -369,8 +373,6 @@ static int msm_ext_disp_hpd(struct platform_device *pdev,
msm_ext_disp_process_audio(ext_disp, type, state, flags);
msm_ext_disp_update_audio_ops(ext_disp, type, state, flags);
msm_ext_disp_process_display(ext_disp, type, state, flags);
-
- ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
}
pr_debug("Hpd (%d) for display (%s)\n", state,
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 2f11c6dd7e05..ddb2388c5006 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016 , The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -67,6 +67,7 @@ static char *debugfs_buf;
static u32 debugfs_buf_size;
static u32 debugfs_buf_used;
static int wraparound;
+static struct mutex sps_debugfs_lock;
struct dentry *dent;
struct dentry *dfile_info;
@@ -85,6 +86,7 @@ static struct sps_bam *phy2bam(phys_addr_t phys_addr);
/* record debug info for debugfs */
void sps_debugfs_record(const char *msg)
{
+ mutex_lock(&sps_debugfs_lock);
if (debugfs_record_enabled) {
if (debugfs_buf_used + MAX_MSG_LEN >= debugfs_buf_size) {
debugfs_buf_used = 0;
@@ -98,6 +100,7 @@ void sps_debugfs_record(const char *msg)
debugfs_buf_size - debugfs_buf_used,
"\n**** end line of sps log ****\n\n");
}
+ mutex_unlock(&sps_debugfs_lock);
}
/* read the recorded debug info to userspace */
@@ -107,6 +110,7 @@ static ssize_t sps_read_info(struct file *file, char __user *ubuf,
int ret = 0;
int size;
+ mutex_lock(&sps_debugfs_lock);
if (debugfs_record_enabled) {
if (wraparound)
size = debugfs_buf_size - MAX_MSG_LEN;
@@ -116,6 +120,7 @@ static ssize_t sps_read_info(struct file *file, char __user *ubuf,
ret = simple_read_from_buffer(ubuf, count, ppos,
debugfs_buf, size);
}
+ mutex_unlock(&sps_debugfs_lock);
return ret;
}
@@ -161,11 +166,13 @@ static ssize_t sps_set_info(struct file *file, const char __user *buf,
new_buf_size = buf_size_kb * SZ_1K;
+ mutex_lock(&sps_debugfs_lock);
if (debugfs_record_enabled) {
if (debugfs_buf_size == new_buf_size) {
/* need do nothing */
pr_info("sps:debugfs: input buffer size "
"is the same as before.\n");
+ mutex_unlock(&sps_debugfs_lock);
return count;
} else {
/* release the current buffer */
@@ -185,12 +192,14 @@ static ssize_t sps_set_info(struct file *file, const char __user *buf,
if (!debugfs_buf) {
debugfs_buf_size = 0;
pr_err("sps:fail to allocate memory for debug_fs.\n");
+ mutex_unlock(&sps_debugfs_lock);
return -ENOMEM;
}
debugfs_buf_used = 0;
wraparound = false;
debugfs_record_enabled = true;
+ mutex_unlock(&sps_debugfs_lock);
return count;
}
@@ -239,6 +248,7 @@ static ssize_t sps_set_logging_option(struct file *file, const char __user *buf,
return count;
}
+ mutex_lock(&sps_debugfs_lock);
if (((option == 0) || (option == 2)) &&
((logging_option == 1) || (logging_option == 3))) {
debugfs_record_enabled = false;
@@ -250,6 +260,7 @@ static ssize_t sps_set_logging_option(struct file *file, const char __user *buf,
}
logging_option = option;
+ mutex_unlock(&sps_debugfs_lock);
return count;
}
@@ -587,6 +598,8 @@ static void sps_debugfs_init(void)
goto bam_log_level_err;
}
+ mutex_init(&sps_debugfs_lock);
+
return;
bam_log_level_err:
diff --git a/drivers/platform/msm/sps/spsi.h b/drivers/platform/msm/sps/spsi.h
index 1b4ca69bee16..38ee76be13c1 100644
--- a/drivers/platform/msm/sps/spsi.h
+++ b/drivers/platform/msm/sps/spsi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -145,11 +145,6 @@ extern u8 print_limit_option;
pr_info(msg, ##args); \
} \
} while (0)
-#define SPS_DEBUGFS(msg, args...) do { \
- char buf[MAX_MSG_LEN]; \
- snprintf(buf, MAX_MSG_LEN, msg"\n", ##args); \
- sps_debugfs_record(buf); \
- } while (0)
#define SPS_ERR(dev, msg, args...) do { \
if (logging_option != 1) { \
if (unlikely(print_limit_option > 2)) \
@@ -157,8 +152,6 @@ extern u8 print_limit_option;
else \
pr_err(msg, ##args); \
} \
- if (unlikely(debugfs_record_enabled)) \
- SPS_DEBUGFS(msg, ##args); \
SPS_IPC(3, dev, msg, args); \
} while (0)
#define SPS_INFO(dev, msg, args...) do { \
@@ -168,8 +161,6 @@ extern u8 print_limit_option;
else \
pr_info(msg, ##args); \
} \
- if (unlikely(debugfs_record_enabled)) \
- SPS_DEBUGFS(msg, ##args); \
SPS_IPC(3, dev, msg, args); \
} while (0)
#define SPS_DBG(dev, msg, args...) do { \
@@ -181,8 +172,6 @@ extern u8 print_limit_option;
pr_info(msg, ##args); \
} else \
pr_debug(msg, ##args); \
- if (unlikely(debugfs_record_enabled)) \
- SPS_DEBUGFS(msg, ##args); \
if (dev) { \
if ((dev)->ipc_loglevel <= 0) \
SPS_IPC(0, dev, msg, args); \
@@ -197,8 +186,6 @@ extern u8 print_limit_option;
pr_info(msg, ##args); \
} else \
pr_debug(msg, ##args); \
- if (unlikely(debugfs_record_enabled)) \
- SPS_DEBUGFS(msg, ##args); \
if (dev) { \
if ((dev)->ipc_loglevel <= 1) \
SPS_IPC(1, dev, msg, args); \
@@ -213,8 +200,6 @@ extern u8 print_limit_option;
pr_info(msg, ##args); \
} else \
pr_debug(msg, ##args); \
- if (unlikely(debugfs_record_enabled)) \
- SPS_DEBUGFS(msg, ##args); \
if (dev) { \
if ((dev)->ipc_loglevel <= 2) \
SPS_IPC(2, dev, msg, args); \
@@ -229,8 +214,6 @@ extern u8 print_limit_option;
pr_info(msg, ##args); \
} else \
pr_debug(msg, ##args); \
- if (unlikely(debugfs_record_enabled)) \
- SPS_DEBUGFS(msg, ##args); \
if (dev) { \
if ((dev)->ipc_loglevel <= 3) \
SPS_IPC(3, dev, msg, args); \
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 5cb017fdf2d3..a151d0c7a770 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3100,79 +3100,6 @@ static int enable_usb_bam(struct platform_device *pdev)
return 0;
}
-static ssize_t
-usb_bam_show_inactivity_timer(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- char *buff = buf;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bam_enable_strings); i++) {
- buff += snprintf(buff, PAGE_SIZE, "%s: %dms\n",
- bam_enable_strings[i],
- msm_usb_bam[i].inactivity_timer_ms);
- }
-
- return buff - buf;
-}
-
-static ssize_t usb_bam_store_inactivity_timer(struct device *dev,
- struct device_attribute *attr,
- const char *buff, size_t count)
-{
- char buf[USB_BAM_MAX_STR_LEN];
- char *trimmed_buf, *bam_str, *bam_name, *timer;
- int timer_d;
- int bam;
-
- if (strnstr(buff, "help", USB_BAM_MAX_STR_LEN)) {
- pr_info("Usage: <bam_name> <ms>,<bam_name> <ms>,...\n");
- pr_info("\tbam_name: [%s, %s, %s]\n",
- bam_enable_strings[DWC3_CTRL],
- bam_enable_strings[CI_CTRL],
- bam_enable_strings[HSIC_CTRL]);
- pr_info("\tms: time in ms. Use 0 to disable timer\n");
- return count;
- }
-
- strlcpy(buf, buff, sizeof(buf));
- trimmed_buf = strim(buf);
-
- while (trimmed_buf) {
- bam_str = strsep(&trimmed_buf, ",");
- if (bam_str) {
- bam_name = strsep(&bam_str, " ");
- bam = get_bam_type_from_core_name(bam_name);
- if (bam < 0 || bam >= MAX_BAMS) {
- log_event_err("%s: Invalid bam, type=%d ,name=%s\n",
- __func__, bam, bam_name);
- return -EINVAL;
- }
-
- timer = strsep(&bam_str, " ");
-
- if (!timer)
- continue;
-
- sscanf(timer, "%d", &timer_d);
-
- /* Apply new timer setting if bam has running pipes */
- if (msm_usb_bam[bam].inactivity_timer_ms != timer_d) {
- msm_usb_bam[bam].inactivity_timer_ms = timer_d;
- if (msm_usb_bam[bam].pipes_enabled_per_bam > 0
- && !info[bam].in_lpm)
- usb_bam_set_inactivity_timer(bam);
- }
- }
- }
-
- return count;
-}
-
-static DEVICE_ATTR(inactivity_timer, S_IWUSR | S_IRUSR,
- usb_bam_show_inactivity_timer,
- usb_bam_store_inactivity_timer);
-
static int usb_bam_panic_notifier(struct notifier_block *this,
unsigned long event, void *ptr)
{
@@ -3221,12 +3148,6 @@ static int usb_bam_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "usb_bam_probe\n");
- ret = device_create_file(&pdev->dev, &dev_attr_inactivity_timer);
- if (ret) {
- dev_err(&pdev->dev, "failed to create fs node\n");
- return ret;
- }
-
io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!io_res) {
dev_err(&pdev->dev, "missing BAM memory resource\n");
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index bd77e55bafc6..0dd6017245fa 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -46,7 +46,7 @@ static ssize_t power_supply_show_property(struct device *dev,
static char *type_text[] = {
"Unknown", "Battery", "UPS", "Mains", "USB", "USB_DCP",
"USB_CDP", "USB_ACA", "USB_HVDCP", "USB_HVDCP_3", "USB_PD",
- "Wireless", "BMS", "Parallel", "Main", "Wipower",
+ "Wireless", "USB_FLOAT", "BMS", "Parallel", "Main", "Wipower",
"TYPEC", "TYPEC_UFP", "TYPEC_DFP"
};
static char *status_text[] = {
diff --git a/drivers/power/qcom/debug_core.c b/drivers/power/qcom/debug_core.c
index ccef04ae9eb2..51b6d63fe994 100644
--- a/drivers/power/qcom/debug_core.c
+++ b/drivers/power/qcom/debug_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -288,8 +288,8 @@ static const struct file_operations msm_core_ptable_ops = {
int msm_core_debug_init(void)
{
- struct dentry *dir;
- struct dentry *file;
+ struct dentry *dir = NULL;
+ struct dentry *file = NULL;
int i;
msm_core_data = get_cpu_pwr_stats();
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index acea4f213484..e17c65c1e4e7 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -47,7 +47,7 @@
#define SCM_EDLOAD_MODE 0X01
#define SCM_DLOAD_CMD 0x10
#define SCM_DLOAD_MINIDUMP 0X20
-
+#define SCM_DLOAD_BOTHDUMPS (SCM_DLOAD_MINIDUMP | SCM_DLOAD_FULLDUMP)
static int restart_mode;
static void *restart_reason;
@@ -488,7 +488,8 @@ static ssize_t show_dload_mode(struct kobject *kobj, struct attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "DLOAD dump type: %s\n",
- (dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full");
+ (dload_type == SCM_DLOAD_BOTHDUMPS) ? "both" :
+ ((dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full"));
}
static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr,
@@ -502,8 +503,16 @@ static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr,
return -ENODEV;
}
dload_type = SCM_DLOAD_MINIDUMP;
- } else {
- pr_err("Invalid value. Use 'full' or 'mini'\n");
+ } else if (sysfs_streq(buf, "both")) {
+ if (!minidump_enabled) {
+ pr_err("Minidump not enabled, setting fulldump only\n");
+ dload_type = SCM_DLOAD_FULLDUMP;
+ return count;
+ }
+ dload_type = SCM_DLOAD_BOTHDUMPS;
+ } else{
+ pr_err("Invalid Dump setup request..\n");
+ pr_err("Supported dumps:'full', 'mini', or 'both'\n");
return -EINVAL;
}
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 38f9f762ccc7..5e8cc84fbfbf 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -550,8 +550,6 @@ static int usb_icl_vote_callback(struct votable *votable, void *data,
power_supply_set_property(chip->main_psy,
POWER_SUPPLY_PROP_CURRENT_MAX,
&pval);
- /* wait for ICL change */
- msleep(20);
}
/* set the effective ICL */
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 25c9d0251cf1..947108c1410e 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -51,9 +51,12 @@
#define PROFILE_LOAD "fg_profile_load"
#define DELTA_SOC "fg_delta_soc"
-/* Delta BSOC votable reasons */
+/* Delta BSOC irq votable reasons */
#define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq"
+/* Battery missing irq votable reasons */
+#define BATT_MISS_IRQ_VOTER "fg_batt_miss_irq"
+
#define DEBUG_PRINT_BUFFER_SIZE 64
/* 3 byte address + 1 space character */
#define ADDR_LEN 4
@@ -219,6 +222,12 @@ enum slope_limit_status {
SLOPE_LIMIT_NUM_COEFFS,
};
+enum esr_timer_config {
+ TIMER_RETRY = 0,
+ TIMER_MAX,
+ NUM_ESR_TIMERS,
+};
+
/* DT parameters for FG device */
struct fg_dt_props {
bool force_load_profile;
@@ -234,9 +243,9 @@ struct fg_dt_props {
int recharge_soc_thr;
int recharge_volt_thr_mv;
int rsense_sel;
- int esr_timer_charging;
- int esr_timer_awake;
- int esr_timer_asleep;
+ int esr_timer_charging[NUM_ESR_TIMERS];
+ int esr_timer_awake[NUM_ESR_TIMERS];
+ int esr_timer_asleep[NUM_ESR_TIMERS];
int rconn_mohms;
int esr_clamp_mohms;
int cl_start_soc;
@@ -355,6 +364,7 @@ struct fg_chip {
struct fg_irq_info *irqs;
struct votable *awake_votable;
struct votable *delta_bsoc_irq_en_votable;
+ struct votable *batt_miss_irq_en_votable;
struct fg_sram_param *sp;
struct fg_alg_flag *alg_flags;
int *debug_mask;
@@ -385,6 +395,7 @@ struct fg_chip {
int maint_soc;
int delta_soc;
int last_msoc;
+ int esr_timer_charging_default[NUM_ESR_TIMERS];
enum slope_limit_status slope_limit_sts;
bool profile_available;
bool profile_loaded;
@@ -460,6 +471,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 26648595c55c..e03681ec13cc 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -491,7 +491,7 @@ static void fg_encode_default(struct fg_sram_param *sp,
int i, mask = 0xff;
int64_t temp;
- temp = DIV_ROUND_CLOSEST(val * sp[id].numrtr, sp[id].denmtr);
+ temp = (int64_t)div_s64((s64)val * sp[id].numrtr, sp[id].denmtr);
pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val);
for (i = 0; i < sp[id].len; i++) {
buf[i] = temp & mask;
@@ -904,6 +904,7 @@ out:
return ret;
}
+ vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, true, 0);
return rc;
}
@@ -1025,12 +1026,15 @@ static inline void get_esr_meas_current(int curr_ma, u8 *val)
*val <<= ESR_PULL_DOWN_IVAL_SHIFT;
}
-static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging,
- int flags)
+static int fg_set_esr_timer(struct fg_chip *chip, int cycles_init,
+ int cycles_max, bool charging, int flags)
{
u8 buf[2];
int rc, timer_max, timer_init;
+ if (cycles_init < 0 || cycles_max < 0)
+ return 0;
+
if (charging) {
timer_max = FG_SRAM_ESR_TIMER_CHG_MAX;
timer_init = FG_SRAM_ESR_TIMER_CHG_INIT;
@@ -1039,7 +1043,7 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging,
timer_init = FG_SRAM_ESR_TIMER_DISCHG_INIT;
}
- fg_encode(chip->sp, timer_max, cycles, buf);
+ fg_encode(chip->sp, timer_max, cycles_max, buf);
rc = fg_sram_write(chip,
chip->sp[timer_max].addr_word,
chip->sp[timer_max].addr_byte, buf,
@@ -1050,7 +1054,7 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging,
return rc;
}
- fg_encode(chip->sp, timer_init, cycles, buf);
+ fg_encode(chip->sp, timer_init, cycles_init, buf);
rc = fg_sram_write(chip,
chip->sp[timer_init].addr_word,
chip->sp[timer_init].addr_byte, buf,
@@ -1061,6 +1065,8 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging,
return rc;
}
+ fg_dbg(chip, FG_STATUS, "esr_%s_timer set to %d/%d\n",
+ charging ? "charging" : "discharging", cycles_init, cycles_max);
return 0;
}
@@ -1098,6 +1104,25 @@ static void fg_notify_charger(struct fg_chip *chip)
fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n");
}
+static int fg_batt_miss_irq_en_cb(struct votable *votable, void *data,
+ int enable, const char *client)
+{
+ struct fg_chip *chip = data;
+
+ if (!chip->irqs[BATT_MISSING_IRQ].irq)
+ return 0;
+
+ if (enable) {
+ enable_irq(chip->irqs[BATT_MISSING_IRQ].irq);
+ enable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq);
+ } else {
+ disable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq);
+ disable_irq(chip->irqs[BATT_MISSING_IRQ].irq);
+ }
+
+ return 0;
+}
+
static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data,
int enable, const char *client)
{
@@ -1315,9 +1340,16 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip)
return rc;
}
- cc_soc_delta_pct = DIV_ROUND_CLOSEST(
- abs(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100,
- CC_SOC_30BIT);
+ cc_soc_delta_pct =
+ div64_s64((int64_t)(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100,
+ CC_SOC_30BIT);
+
+ /* If the delta is < 50%, then skip processing full data */
+ if (cc_soc_delta_pct < 50) {
+ pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct);
+ return -ERANGE;
+ }
+
delta_cc_uah = div64_s64(chip->cl.learned_cc_uah * cc_soc_delta_pct,
100);
chip->cl.final_cc_uah = chip->cl.init_cc_uah + delta_cc_uah;
@@ -1387,10 +1419,10 @@ out:
return rc;
}
-#define FULL_SOC_RAW 255
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);
@@ -1431,11 +1463,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;
+ }
}
}
@@ -1970,7 +2020,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,
@@ -1983,10 +2033,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,
@@ -2039,6 +2086,50 @@ static int fg_esr_fcc_config(struct fg_chip *chip)
return 0;
}
+static int fg_esr_timer_config(struct fg_chip *chip, bool sleep)
+{
+ int rc, cycles_init, cycles_max;
+ bool end_of_charge = false;
+
+ end_of_charge = is_input_present(chip) && chip->charge_done;
+ fg_dbg(chip, FG_STATUS, "sleep: %d eoc: %d\n", sleep, end_of_charge);
+
+ /* ESR discharging timer configuration */
+ cycles_init = sleep ? chip->dt.esr_timer_asleep[TIMER_RETRY] :
+ chip->dt.esr_timer_awake[TIMER_RETRY];
+ if (end_of_charge)
+ cycles_init = 0;
+
+ cycles_max = sleep ? chip->dt.esr_timer_asleep[TIMER_MAX] :
+ chip->dt.esr_timer_awake[TIMER_MAX];
+
+ rc = fg_set_esr_timer(chip, cycles_init, cycles_max, false,
+ sleep ? FG_IMA_NO_WLOCK : FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in setting ESR timer, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* ESR charging timer configuration */
+ cycles_init = cycles_max = -EINVAL;
+ if (end_of_charge || sleep) {
+ cycles_init = chip->dt.esr_timer_charging[TIMER_RETRY];
+ cycles_max = chip->dt.esr_timer_charging[TIMER_MAX];
+ } else if (is_input_present(chip)) {
+ cycles_init = chip->esr_timer_charging_default[TIMER_RETRY];
+ cycles_max = chip->esr_timer_charging_default[TIMER_MAX];
+ }
+
+ rc = fg_set_esr_timer(chip, cycles_init, cycles_max, true,
+ sleep ? FG_IMA_NO_WLOCK : FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in setting ESR timer, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
static void fg_batt_avg_update(struct fg_chip *chip)
{
if (chip->charge_status == chip->prev_charge_status)
@@ -2112,6 +2203,10 @@ static void status_change_work(struct work_struct *work)
if (rc < 0)
pr_err("Error in adjusting FCC for ESR, rc=%d\n", rc);
+ rc = fg_esr_timer_config(chip, false);
+ if (rc < 0)
+ pr_err("Error in configuring ESR timer, rc=%d\n", rc);
+
rc = fg_get_battery_temp(chip, &batt_temp);
if (!rc) {
rc = fg_slope_limit_config(chip, batt_temp);
@@ -2439,6 +2534,23 @@ static void profile_load_work(struct work_struct *work)
int rc;
vote(chip->awake_votable, PROFILE_LOAD, true, 0);
+
+ rc = fg_get_batt_id(chip);
+ if (rc < 0) {
+ pr_err("Error in getting battery id, rc:%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_get_batt_profile(chip);
+ if (rc < 0) {
+ pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n",
+ chip->batt_id_ohms / 1000, rc);
+ goto out;
+ }
+
+ if (!chip->profile_available)
+ goto out;
+
if (!is_profile_load_required(chip))
goto done;
@@ -2503,9 +2615,9 @@ done:
batt_psy_initialized(chip);
fg_notify_charger(chip);
chip->profile_loaded = true;
- chip->soc_reporting_ready = true;
fg_dbg(chip, FG_STATUS, "profile loaded successfully");
out:
+ chip->soc_reporting_ready = true;
vote(chip->awake_votable, PROFILE_LOAD, false, 0);
}
@@ -3024,6 +3136,7 @@ static int fg_psy_set_property(struct power_supply *psy,
pval->intval);
return -EINVAL;
}
+ break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
rc = fg_set_constant_chg_voltage(chip, pval->intval);
break;
@@ -3115,6 +3228,8 @@ static const struct power_supply_desc fg_psy_desc = {
/* INIT FUNCTIONS STAY HERE */
+#define DEFAULT_ESR_CHG_TIMER_RETRY 8
+#define DEFAULT_ESR_CHG_TIMER_MAX 16
static int fg_hw_init(struct fg_chip *chip)
{
int rc;
@@ -3283,22 +3398,29 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
- if (chip->dt.esr_timer_charging > 0) {
- rc = fg_set_esr_timer(chip, chip->dt.esr_timer_charging, true,
- FG_IMA_DEFAULT);
- if (rc < 0) {
- pr_err("Error in setting ESR timer, rc=%d\n", rc);
- return rc;
- }
+ if (chip->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) {
+ chip->esr_timer_charging_default[TIMER_RETRY] =
+ DEFAULT_ESR_CHG_TIMER_RETRY;
+ chip->esr_timer_charging_default[TIMER_MAX] =
+ DEFAULT_ESR_CHG_TIMER_MAX;
+ } else {
+ /* We don't need this for pm660 at present */
+ chip->esr_timer_charging_default[TIMER_RETRY] = -EINVAL;
+ chip->esr_timer_charging_default[TIMER_MAX] = -EINVAL;
}
- if (chip->dt.esr_timer_awake > 0) {
- rc = fg_set_esr_timer(chip, chip->dt.esr_timer_awake, false,
- FG_IMA_DEFAULT);
- if (rc < 0) {
- pr_err("Error in setting ESR timer, rc=%d\n", rc);
- return rc;
- }
+ rc = fg_set_esr_timer(chip, chip->dt.esr_timer_charging[TIMER_RETRY],
+ chip->dt.esr_timer_charging[TIMER_MAX], true, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in setting ESR timer, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_set_esr_timer(chip, chip->dt.esr_timer_awake[TIMER_RETRY],
+ chip->dt.esr_timer_awake[TIMER_MAX], false, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in setting ESR timer, rc=%d\n", rc);
+ return rc;
}
if (chip->cyc_ctr.en)
@@ -3481,20 +3603,6 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
- rc = fg_get_batt_id(chip);
- if (rc < 0) {
- chip->soc_reporting_ready = true;
- pr_err("Error in getting battery id, rc:%d\n", rc);
- return IRQ_HANDLED;
- }
-
- rc = fg_get_batt_profile(chip);
- if (rc < 0) {
- chip->soc_reporting_ready = true;
- pr_err("Error in getting battery profile, rc:%d\n", rc);
- return IRQ_HANDLED;
- }
-
clear_battery_profile(chip);
schedule_delayed_work(&chip->profile_load_work, 0);
@@ -3778,6 +3886,32 @@ static int fg_register_interrupts(struct fg_chip *chip)
return 0;
}
+static int fg_parse_dt_property_u32_array(struct device_node *node,
+ const char *prop_name, int *buf, int len)
+{
+ int rc;
+
+ rc = of_property_count_elems_of_size(node, prop_name, sizeof(u32));
+ if (rc < 0) {
+ if (rc == -EINVAL)
+ return 0;
+ else
+ return rc;
+ } else if (rc != len) {
+ pr_err("Incorrect length %d for %s, rc=%d\n", len, prop_name,
+ rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32_array(node, prop_name, buf, len);
+ if (rc < 0) {
+ pr_err("Error in reading %s, rc=%d\n", prop_name, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
static int fg_parse_slope_limit_coefficients(struct fg_chip *chip)
{
struct device_node *node = chip->dev->of_node;
@@ -3788,17 +3922,10 @@ static int fg_parse_slope_limit_coefficients(struct fg_chip *chip)
if (rc < 0)
return 0;
- rc = of_property_count_elems_of_size(node, "qcom,slope-limit-coeffs",
- sizeof(u32));
- if (rc != SLOPE_LIMIT_NUM_COEFFS)
- return -EINVAL;
-
- rc = of_property_read_u32_array(node, "qcom,slope-limit-coeffs",
- chip->dt.slope_limit_coeffs, SLOPE_LIMIT_NUM_COEFFS);
- if (rc < 0) {
- pr_err("Error in reading qcom,slope-limit-coeffs, rc=%d\n", rc);
+ rc = fg_parse_dt_property_u32_array(node, "qcom,slope-limit-coeffs",
+ chip->dt.slope_limit_coeffs, SLOPE_LIMIT_NUM_COEFFS);
+ if (rc < 0)
return rc;
- }
for (i = 0; i < SLOPE_LIMIT_NUM_COEFFS; i++) {
if (chip->dt.slope_limit_coeffs[i] > SLOPE_LIMIT_COEFF_MAX ||
@@ -3817,44 +3944,20 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip)
struct device_node *node = chip->dev->of_node;
int rc, i;
- rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-soc-dischg",
- sizeof(u32));
- if (rc != KI_COEFF_SOC_LEVELS)
- return 0;
-
- rc = of_property_read_u32_array(node, "qcom,ki-coeff-soc-dischg",
- chip->dt.ki_coeff_soc, KI_COEFF_SOC_LEVELS);
- if (rc < 0) {
- pr_err("Error in reading ki-coeff-soc-dischg, rc=%d\n",
- rc);
+ rc = fg_parse_dt_property_u32_array(node, "qcom,ki-coeff-soc-dischg",
+ chip->dt.ki_coeff_soc, KI_COEFF_SOC_LEVELS);
+ if (rc < 0)
return rc;
- }
-
- rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-med-dischg",
- sizeof(u32));
- if (rc != KI_COEFF_SOC_LEVELS)
- return 0;
- rc = of_property_read_u32_array(node, "qcom,ki-coeff-med-dischg",
- chip->dt.ki_coeff_med_dischg, KI_COEFF_SOC_LEVELS);
- if (rc < 0) {
- pr_err("Error in reading ki-coeff-med-dischg, rc=%d\n",
- rc);
+ rc = fg_parse_dt_property_u32_array(node, "qcom,ki-coeff-med-dischg",
+ chip->dt.ki_coeff_med_dischg, KI_COEFF_SOC_LEVELS);
+ if (rc < 0)
return rc;
- }
-
- rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-hi-dischg",
- sizeof(u32));
- if (rc != KI_COEFF_SOC_LEVELS)
- return 0;
- rc = of_property_read_u32_array(node, "qcom,ki-coeff-hi-dischg",
- chip->dt.ki_coeff_hi_dischg, KI_COEFF_SOC_LEVELS);
- if (rc < 0) {
- pr_err("Error in reading ki-coeff-hi-dischg, rc=%d\n",
- rc);
+ rc = fg_parse_dt_property_u32_array(node, "qcom,ki-coeff-hi-dischg",
+ chip->dt.ki_coeff_hi_dischg, KI_COEFF_SOC_LEVELS);
+ if (rc < 0)
return rc;
- }
for (i = 0; i < KI_COEFF_SOC_LEVELS; i++) {
if (chip->dt.ki_coeff_soc[i] < 0 ||
@@ -4099,23 +4202,26 @@ static int fg_parse_dt(struct fg_chip *chip)
rc);
}
- rc = of_property_read_u32(node, "qcom,fg-esr-timer-charging", &temp);
- if (rc < 0)
- chip->dt.esr_timer_charging = -EINVAL;
- else
- chip->dt.esr_timer_charging = temp;
+ rc = fg_parse_dt_property_u32_array(node, "qcom,fg-esr-timer-charging",
+ chip->dt.esr_timer_charging, NUM_ESR_TIMERS);
+ if (rc < 0) {
+ chip->dt.esr_timer_charging[TIMER_RETRY] = -EINVAL;
+ chip->dt.esr_timer_charging[TIMER_MAX] = -EINVAL;
+ }
- rc = of_property_read_u32(node, "qcom,fg-esr-timer-awake", &temp);
- if (rc < 0)
- chip->dt.esr_timer_awake = -EINVAL;
- else
- chip->dt.esr_timer_awake = temp;
+ rc = fg_parse_dt_property_u32_array(node, "qcom,fg-esr-timer-awake",
+ chip->dt.esr_timer_awake, NUM_ESR_TIMERS);
+ if (rc < 0) {
+ chip->dt.esr_timer_awake[TIMER_RETRY] = -EINVAL;
+ chip->dt.esr_timer_awake[TIMER_MAX] = -EINVAL;
+ }
- rc = of_property_read_u32(node, "qcom,fg-esr-timer-asleep", &temp);
- if (rc < 0)
- chip->dt.esr_timer_asleep = -EINVAL;
- else
- chip->dt.esr_timer_asleep = temp;
+ rc = fg_parse_dt_property_u32_array(node, "qcom,fg-esr-timer-asleep",
+ chip->dt.esr_timer_asleep, NUM_ESR_TIMERS);
+ if (rc < 0) {
+ chip->dt.esr_timer_asleep[TIMER_RETRY] = -EINVAL;
+ chip->dt.esr_timer_asleep[TIMER_MAX] = -EINVAL;
+ }
chip->cyc_ctr.en = of_property_read_bool(node, "qcom,cycle-counter-en");
if (chip->cyc_ctr.en)
@@ -4263,6 +4369,9 @@ static void fg_cleanup(struct fg_chip *chip)
if (chip->delta_bsoc_irq_en_votable)
destroy_votable(chip->delta_bsoc_irq_en_votable);
+ if (chip->batt_miss_irq_en_votable)
+ destroy_votable(chip->batt_miss_irq_en_votable);
+
if (chip->batt_id_chan)
iio_channel_release(chip->batt_id_chan);
@@ -4320,6 +4429,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
chip);
if (IS_ERR(chip->awake_votable)) {
rc = PTR_ERR(chip->awake_votable);
+ chip->awake_votable = NULL;
goto exit;
}
@@ -4328,6 +4438,16 @@ static int fg_gen3_probe(struct platform_device *pdev)
fg_delta_bsoc_irq_en_cb, chip);
if (IS_ERR(chip->delta_bsoc_irq_en_votable)) {
rc = PTR_ERR(chip->delta_bsoc_irq_en_votable);
+ chip->delta_bsoc_irq_en_votable = NULL;
+ goto exit;
+ }
+
+ chip->batt_miss_irq_en_votable = create_votable("FG_BATT_MISS_IRQ",
+ VOTE_SET_ANY,
+ fg_batt_miss_irq_en_cb, chip);
+ if (IS_ERR(chip->batt_miss_irq_en_votable)) {
+ rc = PTR_ERR(chip->batt_miss_irq_en_votable);
+ chip->batt_miss_irq_en_votable = NULL;
goto exit;
}
@@ -4352,19 +4472,6 @@ static int fg_gen3_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work);
INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
- rc = fg_get_batt_id(chip);
- if (rc < 0) {
- pr_err("Error in getting battery id, rc:%d\n", rc);
- goto exit;
- }
-
- rc = fg_get_batt_profile(chip);
- if (rc < 0) {
- chip->soc_reporting_ready = true;
- pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n",
- chip->batt_id_ohms / 1000, rc);
- }
-
rc = fg_memif_init(chip);
if (rc < 0) {
dev_err(chip->dev, "Error in initializing FG_MEMIF, rc:%d\n",
@@ -4408,12 +4515,15 @@ static int fg_gen3_probe(struct platform_device *pdev)
goto exit;
}
- /* Keep SOC_UPDATE irq disabled until we require it */
+ /* Keep SOC_UPDATE_IRQ disabled until we require it */
if (fg_irqs[SOC_UPDATE_IRQ].irq)
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);
+ /* Keep BSOC_DELTA_IRQ disabled until we require it */
+ vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);
+
+ /* Keep BATT_MISSING_IRQ disabled until we require it */
+ vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0);
rc = fg_debugfs_create(chip);
if (rc < 0) {
@@ -4438,8 +4548,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
}
device_init_wakeup(chip->dev, true);
- if (chip->profile_available)
- schedule_delayed_work(&chip->profile_load_work, 0);
+ schedule_delayed_work(&chip->profile_load_work, 0);
pr_debug("FG GEN3 driver probed successfully\n");
return 0;
@@ -4453,15 +4562,9 @@ static int fg_gen3_suspend(struct device *dev)
struct fg_chip *chip = dev_get_drvdata(dev);
int rc;
- if (chip->dt.esr_timer_awake > 0 && chip->dt.esr_timer_asleep > 0) {
- rc = fg_set_esr_timer(chip, chip->dt.esr_timer_asleep, false,
- FG_IMA_NO_WLOCK);
- if (rc < 0) {
- pr_err("Error in setting ESR timer during suspend, rc=%d\n",
- rc);
- return rc;
- }
- }
+ rc = fg_esr_timer_config(chip, true);
+ if (rc < 0)
+ pr_err("Error in configuring ESR timer, rc=%d\n", rc);
cancel_delayed_work_sync(&chip->batt_avg_work);
if (fg_sram_dump)
@@ -4474,15 +4577,9 @@ static int fg_gen3_resume(struct device *dev)
struct fg_chip *chip = dev_get_drvdata(dev);
int rc;
- if (chip->dt.esr_timer_awake > 0 && chip->dt.esr_timer_asleep > 0) {
- rc = fg_set_esr_timer(chip, chip->dt.esr_timer_awake, false,
- FG_IMA_DEFAULT);
- if (rc < 0) {
- pr_err("Error in setting ESR timer during resume, rc=%d\n",
- rc);
- return rc;
- }
- }
+ rc = fg_esr_timer_config(chip, false);
+ if (rc < 0)
+ pr_err("Error in configuring ESR timer, rc=%d\n", rc);
fg_circ_buf_clr(&chip->ibatt_circ_buf);
fg_circ_buf_clr(&chip->vbatt_circ_buf);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 803851d2b3ef..0908eea3b186 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -266,6 +266,10 @@ module_param_named(
debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR
);
+static int __weak_chg_icl_ua = 500000;
+module_param_named(
+ weak_chg_icl_ua, __weak_chg_icl_ua, int, S_IRUSR | S_IWUSR);
+
#define MICRO_1P5A 1500000
#define MICRO_P1A 100000
#define OTG_DEFAULT_DEGLITCH_TIME_MS 50
@@ -461,6 +465,8 @@ static int smb2_usb_get_prop(struct power_supply *psy,
val->intval = 0;
else
val->intval = 1;
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN)
+ val->intval = 0;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
val->intval = chg->voltage_min_uv;
@@ -1462,15 +1468,6 @@ static int smb2_configure_typec(struct smb_charger *chg)
return rc;
}
- /* configure power role for dual-role */
- rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
- TYPEC_POWER_ROLE_CMD_MASK, 0);
- if (rc < 0) {
- dev_err(chg->dev,
- "Couldn't configure power role for DRP rc=%d\n", rc);
- return rc;
- }
-
/*
* disable Type-C factory mode and stay in Attached.SRC state when VCONN
* over-current happens
@@ -1848,6 +1845,16 @@ static int smb2_init_hw(struct smb2 *chip)
static int smb2_post_init(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
+ int rc;
+
+ /* configure power role for dual-role */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, 0);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure power role for DRP rc=%d\n", rc);
+ return rc;
+ }
rerun_election(chg->usb_irq_enable_votable);
@@ -2109,7 +2116,7 @@ static struct smb_irq_info smb2_irqs[] = {
[SWITCH_POWER_OK_IRQ] = {
.name = "switcher-power-ok",
.handler = smblib_handle_switcher_power_ok,
- .storm_data = {true, 1000, 3},
+ .storm_data = {true, 1000, 8},
},
};
@@ -2303,6 +2310,7 @@ static int smb2_probe(struct platform_device *pdev)
chg->dev = &pdev->dev;
chg->param = v1_params;
chg->debug_mask = &__debug_mask;
+ chg->weak_chg_icl_ua = &__weak_chg_icl_ua;
chg->mode = PARALLEL_MASTER;
chg->irq_info = smb2_irqs;
chg->name = "PMI";
@@ -2414,7 +2422,11 @@ static int smb2_probe(struct platform_device *pdev)
goto cleanup;
}
- smb2_post_init(chip);
+ rc = smb2_post_init(chip);
+ if (rc < 0) {
+ pr_err("Failed in post init rc=%d\n", rc);
+ goto cleanup;
+ }
smb2_create_debugfs(chip);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 7848ca4396d9..16db425514c3 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -261,7 +261,7 @@ static const struct apsd_result const smblib_apsd_results[] = {
[FLOAT] = {
.name = "FLOAT",
.bit = FLOAT_CHARGER_BIT,
- .pst = POWER_SUPPLY_TYPE_USB_DCP
+ .pst = POWER_SUPPLY_TYPE_USB_FLOAT
},
[HVDCP2] = {
.name = "HVDCP2",
@@ -630,8 +630,29 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param,
static void smblib_uusb_removal(struct smb_charger *chg)
{
int rc;
+ struct smb_irq_data *data;
+ struct storm_watch *wdata;
cancel_delayed_work_sync(&chg->pl_enable_work);
+
+ if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
+ rc = regulator_disable(chg->dpdm_reg);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
+ rc);
+ }
+
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
+ if (data) {
+ wdata = &data->storm_data;
+ update_storm_count(wdata, WEAK_CHG_STORM_COUNT);
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ false, 0);
+ }
+ }
vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
@@ -737,7 +758,7 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg)
return 0;
}
-static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count)
+static int smblib_get_hw_pulse_cnt(struct smb_charger *chg, int *count)
{
int rc;
u8 val[2];
@@ -771,6 +792,24 @@ static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count)
return 0;
}
+static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count)
+{
+ int rc;
+
+ /* Use software based pulse count if HW INOV is disabled */
+ if (get_effective_result(chg->hvdcp_hw_inov_dis_votable) > 0) {
+ *count = chg->pulse_cnt;
+ return 0;
+ }
+
+ /* Use h/w pulse count if autonomous mode is enabled */
+ rc = smblib_get_hw_pulse_cnt(chg, count);
+ if (rc < 0)
+ smblib_err(chg, "failed to read h/w pulse count rc=%d\n", rc);
+
+ return rc;
+}
+
#define USBIN_25MA 25000
#define USBIN_100MA 100000
#define USBIN_150MA 150000
@@ -1127,7 +1166,7 @@ static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable,
* the pulse count register get zeroed when autonomous mode is
* disabled. Track that in variables before disabling
*/
- rc = smblib_get_pulse_cnt(chg, &chg->pulse_cnt);
+ rc = smblib_get_hw_pulse_cnt(chg, &chg->pulse_cnt);
if (rc < 0) {
pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n",
rc);
@@ -2303,7 +2342,6 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
{
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
int rc, pulses;
- u8 stat;
val->intval = MICRO_5V;
if (apsd_result == NULL) {
@@ -2313,13 +2351,12 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
switch (apsd_result->pst) {
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
- rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat);
+ rc = smblib_get_pulse_cnt(chg, &pulses);
if (rc < 0) {
smblib_err(chg,
"Couldn't read QC_PULSE_COUNT rc=%d\n", rc);
return 0;
}
- pulses = (stat & QC_PULSE_COUNT_MASK);
val->intval = MICRO_5V + HVDCP3_STEP_UV * pulses;
break;
default:
@@ -2824,13 +2861,13 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg,
* USB MAIN PSY SETTERS *
*************************/
-#define SDP_CURRENT_MA 500000
-#define CDP_CURRENT_MA 1500000
-#define DCP_CURRENT_MA 1500000
-#define HVDCP_CURRENT_MA 3000000
-#define TYPEC_DEFAULT_CURRENT_MA 900000
-#define TYPEC_MEDIUM_CURRENT_MA 1500000
-#define TYPEC_HIGH_CURRENT_MA 3000000
+#define SDP_CURRENT_UA 500000
+#define CDP_CURRENT_UA 1500000
+#define DCP_CURRENT_UA 1500000
+#define HVDCP_CURRENT_UA 3000000
+#define TYPEC_DEFAULT_CURRENT_UA 900000
+#define TYPEC_MEDIUM_CURRENT_UA 1500000
+#define TYPEC_HIGH_CURRENT_UA 3000000
int smblib_get_charge_current(struct smb_charger *chg,
int *total_current_ua)
{
@@ -2864,19 +2901,19 @@ int smblib_get_charge_current(struct smb_charger *chg,
/* QC 2.0/3.0 adapter */
if (apsd_result->bit & (QC_3P0_BIT | QC_2P0_BIT)) {
- *total_current_ua = HVDCP_CURRENT_MA;
+ *total_current_ua = HVDCP_CURRENT_UA;
return 0;
}
if (non_compliant) {
switch (apsd_result->bit) {
case CDP_CHARGER_BIT:
- current_ua = CDP_CURRENT_MA;
+ current_ua = CDP_CURRENT_UA;
break;
case DCP_CHARGER_BIT:
case OCP_CHARGER_BIT:
case FLOAT_CHARGER_BIT:
- current_ua = DCP_CURRENT_MA;
+ current_ua = DCP_CURRENT_UA;
break;
default:
current_ua = 0;
@@ -2891,7 +2928,7 @@ int smblib_get_charge_current(struct smb_charger *chg,
case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
switch (apsd_result->bit) {
case CDP_CHARGER_BIT:
- current_ua = CDP_CURRENT_MA;
+ current_ua = CDP_CURRENT_UA;
break;
case DCP_CHARGER_BIT:
case OCP_CHARGER_BIT:
@@ -2904,10 +2941,10 @@ int smblib_get_charge_current(struct smb_charger *chg,
}
break;
case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- current_ua = TYPEC_MEDIUM_CURRENT_MA;
+ current_ua = TYPEC_MEDIUM_CURRENT_UA;
break;
case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- current_ua = TYPEC_HIGH_CURRENT_MA;
+ current_ua = TYPEC_HIGH_CURRENT_UA;
break;
case POWER_SUPPLY_TYPEC_NON_COMPLIANT:
case POWER_SUPPLY_TYPEC_NONE:
@@ -3111,6 +3148,8 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg)
int rc;
u8 stat;
bool vbus_rising;
+ struct smb_irq_data *data;
+ struct storm_watch *wdata;
rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
if (rc < 0) {
@@ -3120,10 +3159,23 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg)
vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
- if (vbus_rising)
+ if (vbus_rising) {
smblib_cc2_sink_removal_exit(chg);
- else
+ } else {
smblib_cc2_sink_removal_enter(chg);
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
+ if (data) {
+ wdata = &data->storm_data;
+ update_storm_count(wdata,
+ WEAK_CHG_STORM_COUNT);
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER,
+ false, 0);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ false, 0);
+ }
+ }
+ }
power_supply_changed(chg->usb_psy);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
@@ -3136,6 +3188,8 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
int rc;
u8 stat;
bool vbus_rising;
+ struct smb_irq_data *data;
+ struct storm_watch *wdata;
rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
if (rc < 0) {
@@ -3172,8 +3226,18 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
schedule_delayed_work(&chg->pl_enable_work,
msecs_to_jiffies(PL_DELAY_MS));
} else {
- if (chg->wa_flags & BOOST_BACK_WA)
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
+ if (data) {
+ wdata = &data->storm_data;
+ update_storm_count(wdata,
+ WEAK_CHG_STORM_COUNT);
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER,
+ false, 0);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ false, 0);
+ }
+ }
if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
@@ -3296,13 +3360,12 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg)
}
if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) {
- rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat);
+ rc = smblib_get_pulse_cnt(chg, &pulses);
if (rc < 0) {
smblib_err(chg,
"Couldn't read QC_PULSE_COUNT rc=%d\n", rc);
return;
}
- pulses = (stat & QC_PULSE_COUNT_MASK);
if (pulses < QC3_PULSES_FOR_6V)
smblib_set_opt_freq_buck(chg,
@@ -3403,8 +3466,29 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
rising ? "rising" : "falling");
}
+static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode)
+{
+ int rp_ua;
+
+ switch (typec_mode) {
+ case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+ rp_ua = TYPEC_HIGH_CURRENT_UA;
+ break;
+ case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+ /* fall through */
+ default:
+ rp_ua = DCP_CURRENT_UA;
+ }
+
+ return rp_ua;
+}
+
static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
{
+ int typec_mode;
+ int rp_ua;
+
/* while PD is active it should have complete ICL control */
if (chg->pd_active)
return;
@@ -3425,7 +3509,10 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
break;
case POWER_SUPPLY_TYPE_USB_DCP:
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
+ case POWER_SUPPLY_TYPE_USB_FLOAT:
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua);
break;
case POWER_SUPPLY_TYPE_USB_HVDCP:
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
@@ -3553,9 +3640,30 @@ static void typec_sink_removal(struct smb_charger *chg)
static void smblib_handle_typec_removal(struct smb_charger *chg)
{
int rc;
+ struct smb_irq_data *data;
+ struct storm_watch *wdata;
chg->cc2_detach_wa_active = false;
+ if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
+ rc = regulator_disable(chg->dpdm_reg);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
+ rc);
+ }
+
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
+ if (data) {
+ wdata = &data->storm_data;
+ update_storm_count(wdata, WEAK_CHG_STORM_COUNT);
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ false, 0);
+ }
+ }
+
/* reset APSD voters */
vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
@@ -3600,6 +3708,11 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
chg->pd_hard_reset = 0;
chg->typec_legacy_valid = false;
+ /* reset back to 120mS tCC debounce */
+ rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't set 120mS tCC debounce rc=%d\n", rc);
+
/* enable APSD CC trigger for next insertion */
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
APSD_START_ON_CC_BIT, APSD_START_ON_CC_BIT);
@@ -3651,6 +3764,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
unlock:
mutex_unlock(&chg->vconn_oc_lock);
+ /* clear exit sink based on cc */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ EXIT_SNK_BASED_ON_CC_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't clear exit_sink_based_on_cc rc=%d\n",
+ rc);
+
typec_sink_removal(chg);
smblib_update_usb_type(chg);
}
@@ -3673,12 +3793,40 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg)
typec_sink_removal(chg);
}
+static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
+{
+ int rp_ua;
+ const struct apsd_result *apsd = smblib_get_apsd_result(chg);
+
+ if ((apsd->pst != POWER_SUPPLY_TYPE_USB_DCP)
+ && (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT))
+ return;
+
+ /*
+ * handle Rp change for DCP/FLOAT/OCP.
+ * Update the current only if the Rp is different from
+ * the last Rp value.
+ */
+ smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n",
+ chg->typec_mode, typec_mode);
+
+ rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua);
+}
+
static void smblib_handle_typec_cc_state_change(struct smb_charger *chg)
{
+ int typec_mode;
+
if (chg->pr_swap_in_progress)
return;
- chg->typec_mode = smblib_get_prop_typec_mode(chg);
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ if (chg->typec_present && (typec_mode != chg->typec_mode))
+ smblib_handle_rp_change(chg, typec_mode);
+
+ chg->typec_mode = typec_mode;
+
if (!chg->typec_present && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) {
chg->typec_present = true;
smblib_dbg(chg, PR_MISC, "TypeC %s insertion\n",
@@ -3764,10 +3912,23 @@ irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data)
return IRQ_HANDLED;
}
+static void smblib_bb_removal_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ bb_removal_work.work);
+
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
+ vote(chg->awake_votable, BOOST_BACK_VOTER, false, 0);
+}
+
+#define BOOST_BACK_UNVOTE_DELAY_MS 750
+#define BOOST_BACK_STORM_COUNT 3
+#define WEAK_CHG_STORM_COUNT 8
irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
+ struct storm_watch *wdata = &irq_data->storm_data;
int rc, usb_icl;
u8 stat;
@@ -3789,8 +3950,32 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
return IRQ_HANDLED;
if (is_storming(&irq_data->storm_data)) {
- smblib_err(chg, "Reverse boost detected: voting 0mA to suspend input\n");
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0);
+ /* This could be a weak charger reduce ICL */
+ if (!is_client_vote_enabled(chg->usb_icl_votable,
+ WEAK_CHARGER_VOTER)) {
+ smblib_err(chg,
+ "Weak charger detected: voting %dmA ICL\n",
+ *chg->weak_chg_icl_ua / 1000);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ true, *chg->weak_chg_icl_ua);
+ /*
+ * reset storm data and set the storm threshold
+ * to 3 for reverse boost detection.
+ */
+ update_storm_count(wdata, BOOST_BACK_STORM_COUNT);
+ } else {
+ smblib_err(chg,
+ "Reverse boost detected: voting 0mA to suspend input\n");
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0);
+ vote(chg->awake_votable, BOOST_BACK_VOTER, true, 0);
+ /*
+ * Remove the boost-back vote after a delay, to avoid
+ * permanently suspending the input if the boost-back
+ * condition is unintentionally hit.
+ */
+ schedule_delayed_work(&chg->bb_removal_work,
+ msecs_to_jiffies(BOOST_BACK_UNVOTE_DELAY_MS));
+ }
}
return IRQ_HANDLED;
@@ -3824,12 +4009,18 @@ int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg,
int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
const union power_supply_propval *val)
{
+ int rc;
+
chg->pr_swap_in_progress = val->intval;
/*
* call the cc changed irq to handle real removals while
* PR_SWAP was in progress
*/
smblib_usb_typec_change(chg);
+ rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT,
+ val->intval ? TCC_DEBOUNCE_20MS_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc);
return 0;
}
@@ -4228,6 +4419,7 @@ static void smblib_legacy_detection_work(struct work_struct *work)
unlock:
chg->typec_en_dis_active = 0;
+ smblib_usb_typec_change(chg);
mutex_unlock(&chg->lock);
}
@@ -4438,6 +4630,7 @@ int smblib_init(struct smb_charger *chg)
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work);
+ INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work);
chg->fake_capacity = -EINVAL;
chg->fake_input_current_limited = -EINVAL;
@@ -4493,6 +4686,7 @@ int smblib_deinit(struct smb_charger *chg)
cancel_delayed_work_sync(&chg->pl_enable_work);
cancel_work_sync(&chg->legacy_detection_work);
cancel_delayed_work_sync(&chg->uusb_otg_work);
+ cancel_delayed_work_sync(&chg->bb_removal_work);
power_supply_unreg_notifier(&chg->nb);
smblib_destroy_votables(chg);
qcom_batt_deinit();
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 8ba0a1dfe19f..c97b84c34084 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -64,9 +64,12 @@ enum print_reason {
#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER"
#define OTG_DELAY_VOTER "OTG_DELAY_VOTER"
#define USBIN_I_VOTER "USBIN_I_VOTER"
+#define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
+#define BOOST_BACK_STORM_COUNT 3
+#define WEAK_CHG_STORM_COUNT 8
enum smb_mode {
PARALLEL_MASTER = 0,
@@ -230,6 +233,7 @@ struct smb_charger {
struct smb_chg_freq chg_freq;
int smb_version;
int otg_delay_ms;
+ int *weak_chg_icl_ua;
/* locks */
struct mutex lock;
@@ -292,6 +296,7 @@ struct smb_charger {
struct delayed_work pl_enable_work;
struct work_struct legacy_detection_work;
struct delayed_work uusb_otg_work;
+ struct delayed_work bb_removal_work;
/* cached status */
int voltage_min_uv;
diff --git a/drivers/power/supply/qcom/smb1351-charger.c b/drivers/power/supply/qcom/smb1351-charger.c
index 7014fd706d9e..f5c8252b5e41 100644
--- a/drivers/power/supply/qcom/smb1351-charger.c
+++ b/drivers/power/supply/qcom/smb1351-charger.c
@@ -1416,6 +1416,7 @@ static enum power_supply_property smb1351_parallel_properties[] = {
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_PARALLEL_MODE,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND,
};
static int smb1351_parallel_set_chg_suspend(struct smb1351_charger *chip,
@@ -1702,6 +1703,9 @@ static int smb1351_parallel_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_PARALLEL_MODE:
val->intval = chip->parallel_mode;
break;
+ case POWER_SUPPLY_PROP_INPUT_SUSPEND:
+ val->intval = chip->parallel_charger_suspended;
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 0ee53b100ae2..335b160e24a5 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -941,13 +941,6 @@ static int smb138x_init_slave_hw(struct smb138x *chip)
return rc;
}
- rc = smblib_write(chg, THERMREG_SRC_CFG_REG,
- THERMREG_SKIN_ADC_SRC_EN_BIT);
- if (rc < 0) {
- pr_err("Couldn't enable connector thermreg source rc=%d\n", rc);
- return rc;
- }
-
return 0;
}
diff --git a/drivers/power/supply/qcom/storm-watch.c b/drivers/power/supply/qcom/storm-watch.c
index 5275079c53e0..21ac669f2ec9 100644
--- a/drivers/power/supply/qcom/storm-watch.c
+++ b/drivers/power/supply/qcom/storm-watch.c
@@ -64,3 +64,13 @@ void reset_storm_count(struct storm_watch *data)
data->storm_count = 0;
mutex_unlock(&data->storm_lock);
}
+
+void update_storm_count(struct storm_watch *data, int max_count)
+{
+ if (!data)
+ return;
+
+ mutex_lock(&data->storm_lock);
+ data->max_storm_count = max_count;
+ mutex_unlock(&data->storm_lock);
+}
diff --git a/drivers/power/supply/qcom/storm-watch.h b/drivers/power/supply/qcom/storm-watch.h
index ff05c4a661c3..5275d73613d4 100644
--- a/drivers/power/supply/qcom/storm-watch.h
+++ b/drivers/power/supply/qcom/storm-watch.h
@@ -37,4 +37,5 @@ struct storm_watch {
bool is_storming(struct storm_watch *data);
void reset_storm_count(struct storm_watch *data);
+void update_storm_count(struct storm_watch *data, int max_count);
#endif
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/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c
index 9a20b6925877..21381d1ad7fa 100644
--- a/drivers/regulator/cpr3-hmss-regulator.c
+++ b/drivers/regulator/cpr3-hmss-regulator.c
@@ -1505,7 +1505,7 @@ static int cpr3_hmss_init_regulator(struct cpr3_regulator *vreg)
static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl)
{
struct cpr3_msm8996_hmss_fuses *fuse = NULL;
- struct cpr3_regulator *vreg;
+ struct cpr3_regulator *vreg = NULL;
u32 aging_ro_scale;
int i, j, rc;
@@ -1710,7 +1710,7 @@ static int cpr3_hmss_regulator_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
const struct of_device_id *match;
struct cpr3_controller *ctrl;
- struct cpr3_regulator *vreg;
+ struct cpr3_regulator *vreg = NULL;
int i, j, rc;
if (!dev->of_node) {
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index d131a8ea4144..3adbd41ede88 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -3117,7 +3117,8 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)
struct cpr4_sdelta *sdelta;
bool valid = false;
bool thread_valid;
- int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt;
+ int i, j, rc;
+ int new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt = 0;
u32 reg_last_measurement = 0, sdelta_size;
int *sdelta_table, *boost_table;
diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c
index 2307da9497dc..ecf7885a4bff 100644
--- a/drivers/regulator/cprh-kbss-regulator.c
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -1975,7 +1975,7 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg)
static int cprh_kbss_init_aging(struct cpr3_controller *ctrl)
{
struct cprh_kbss_fuses *fuse = NULL;
- struct cpr3_regulator *vreg;
+ struct cpr3_regulator *vreg = NULL;
u32 aging_ro_scale;
int i, j, rc = 0;
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/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c
index bd706658348d..08e991fa7db3 100644
--- a/drivers/regulator/qpnp-regulator.c
+++ b/drivers/regulator/qpnp-regulator.c
@@ -127,6 +127,9 @@ enum qpnp_common_regulator_registers {
QPNP_COMMON_REG_ENABLE = 0x46,
QPNP_COMMON_REG_PULL_DOWN = 0x48,
QPNP_COMMON_REG_STEP_CTRL = 0x61,
+ QPNP_COMMON_REG_UL_LL_CTRL = 0x68,
+ QPNP_COMMON_REG_VOLTAGE_ULS_VALID = 0x6A,
+ QPNP_COMMON_REG_VOLTAGE_LLS_VALID = 0x6C,
};
/*
@@ -139,6 +142,8 @@ enum qpnp_common2_regulator_registers {
QPNP_COMMON2_REG_VOLTAGE_MSB = 0x41,
QPNP_COMMON2_REG_MODE = 0x45,
QPNP_COMMON2_REG_STEP_CTRL = 0x61,
+ QPNP_COMMON2_REG_VOLTAGE_ULS_LSB = 0x68,
+ QPNP_COMMON2_REG_VOLTAGE_ULS_MSB = 0x69,
};
enum qpnp_ldo_registers {
@@ -205,6 +210,10 @@ enum qpnp_common2_control_register_index {
/* Common regulator pull down control register layout */
#define QPNP_COMMON_PULL_DOWN_ENABLE_MASK 0x80
+/* Common regulator UL & LL limits control register layout */
+#define QPNP_COMMON_UL_EN_MASK 0x80
+#define QPNP_COMMON_LL_EN_MASK 0x40
+
/* LDO regulator current limit control register layout */
#define QPNP_LDO_CURRENT_LIMIT_ENABLE_MASK 0x80
@@ -1749,6 +1758,89 @@ static int qpnp_regulator_match(struct qpnp_regulator *vreg)
return rc;
}
+static int qpnp_regulator_check_constraints(struct qpnp_regulator *vreg,
+ struct qpnp_regulator_platform_data *pdata)
+{
+ struct qpnp_voltage_range *range = NULL;
+ int i, rc = 0, limit_min_uV, limit_max_uV, max_uV;
+ u8 reg[2];
+
+ limit_min_uV = 0;
+ limit_max_uV = INT_MAX;
+
+ if (vreg->logical_type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) {
+ max_uV = pdata->init_data.constraints.max_uV;
+ /* Find the range which max_uV is inside of. */
+ for (i = vreg->set_points->count - 1; i > 0; i--) {
+ range = &vreg->set_points->range[i];
+ if (range->set_point_max_uV > 0
+ && max_uV >= range->set_point_min_uV
+ && max_uV <= range->set_point_max_uV)
+ break;
+ }
+
+ if (i < 0 || range == NULL) {
+ vreg_err(vreg, "max_uV doesn't fit in any voltage range\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_UL_LL_CTRL,
+ &reg[0], 1);
+ if (rc) {
+ vreg_err(vreg, "UL_LL register read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (reg[0] & QPNP_COMMON_UL_EN_MASK) {
+ rc = qpnp_vreg_read(vreg,
+ QPNP_COMMON_REG_VOLTAGE_ULS_VALID,
+ &reg[1], 1);
+ if (rc) {
+ vreg_err(vreg, "ULS_VALID register read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ limit_max_uV = range->step_uV * reg[1] + range->min_uV;
+ }
+
+ if (reg[0] & QPNP_COMMON_LL_EN_MASK) {
+ rc = qpnp_vreg_read(vreg,
+ QPNP_COMMON_REG_VOLTAGE_LLS_VALID,
+ &reg[1], 1);
+ if (rc) {
+ vreg_err(vreg, "LLS_VALID register read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ limit_min_uV = range->step_uV * reg[1] + range->min_uV;
+ }
+ } else if (vreg->logical_type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS2) {
+ rc = qpnp_vreg_read(vreg, QPNP_COMMON2_REG_VOLTAGE_ULS_LSB,
+ reg, 2);
+ if (rc) {
+ vreg_err(vreg, "ULS registers read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ limit_max_uV = (((int)reg[1] << 8) | (int)reg[0]) * 1000;
+ }
+
+ if (pdata->init_data.constraints.min_uV < limit_min_uV
+ || pdata->init_data.constraints.max_uV > limit_max_uV) {
+ vreg_err(vreg, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n",
+ pdata->init_data.constraints.min_uV,
+ pdata->init_data.constraints.max_uV,
+ limit_min_uV, limit_max_uV);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int qpnp_regulator_ftsmps_init_slew_rate(struct qpnp_regulator *vreg)
{
int rc;
@@ -2282,6 +2374,13 @@ static int qpnp_regulator_probe(struct platform_device *pdev)
}
}
+ rc = qpnp_regulator_check_constraints(vreg, pdata);
+ if (rc) {
+ vreg_err(vreg, "regulator constraints check failed, rc=%d\n",
+ rc);
+ goto bail;
+ }
+
rc = qpnp_regulator_init_registers(vreg, pdata);
if (rc) {
vreg_err(vreg, "common initialization failed, rc=%d\n", rc);
diff --git a/drivers/regulator/rpm-smd-regulator.c b/drivers/regulator/rpm-smd-regulator.c
index d26fd3bea788..a5dc1b983bcb 100644
--- a/drivers/regulator/rpm-smd-regulator.c
+++ b/drivers/regulator/rpm-smd-regulator.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
@@ -207,6 +207,7 @@ struct rpm_regulator {
bool use_pin_ctrl_for_enable;
struct rpm_vreg_request req;
int system_load;
+ int hpm_threshold_current;
int min_uV;
int max_uV;
u32 pin_ctrl_mask[RPM_VREG_PIN_CTRL_STATE_COUNT];
@@ -1030,6 +1031,34 @@ static unsigned int rpm_vreg_get_bob_mode(struct regulator_dev *rdev)
return mode;
}
+static unsigned int rpm_vreg_get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ u32 mode = REGULATOR_MODE_NORMAL;
+
+ if (reg->hpm_threshold_current > 0) {
+ if (load_uA >= reg->hpm_threshold_current) {
+ /* PWM mode */
+ mode = (reg->rpm_vreg->regulator_type
+ == RPM_REGULATOR_TYPE_BOB)
+ ? REGULATOR_MODE_FAST
+ : REGULATOR_MODE_NORMAL;
+ } else {
+ /* AUTO mode */
+ mode = (reg->rpm_vreg->regulator_type
+ == RPM_REGULATOR_TYPE_BOB)
+ ? REGULATOR_MODE_NORMAL
+ : REGULATOR_MODE_IDLE;
+ }
+ } else {
+ /* Default to the current mode if no threshold is present. */
+ mode = reg->rdesc.ops->get_mode(rdev);
+ }
+
+ return mode;
+}
+
static int rpm_vreg_enable_time(struct regulator_dev *rdev)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
@@ -1402,6 +1431,18 @@ static struct regulator_ops smps_ops = {
.enable_time = rpm_vreg_enable_time,
};
+static struct regulator_ops smps_optimum_mode_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage,
+ .get_voltage = rpm_vreg_get_voltage,
+ .set_mode = rpm_vreg_set_mode,
+ .get_mode = rpm_vreg_get_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
+ .enable_time = rpm_vreg_enable_time,
+};
+
static struct regulator_ops switch_ops = {
.enable = rpm_vreg_enable,
.disable = rpm_vreg_disable,
@@ -1426,6 +1467,7 @@ static struct regulator_ops bob_ops = {
.get_voltage = rpm_vreg_get_voltage,
.set_mode = rpm_vreg_set_bob_mode,
.get_mode = rpm_vreg_get_bob_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
.enable_time = rpm_vreg_enable_time,
};
@@ -1676,6 +1718,12 @@ static int rpm_vreg_device_probe(struct platform_device *pdev)
if (of_get_property(node, "parent-supply", NULL))
init_data->supply_regulator = "parent";
+ of_property_read_u32(node, "qcom,pwm-threshold-current",
+ &reg->hpm_threshold_current);
+ if (reg->hpm_threshold_current > 0
+ && regulator_type == RPM_REGULATOR_TYPE_SMPS)
+ reg->rdesc.ops = &smps_optimum_mode_ops;
+
/*
* Fill in ops and mode masks based on callbacks specified for
* this type of regulator.
@@ -1689,8 +1737,13 @@ static int rpm_vreg_device_probe(struct platform_device *pdev)
if (reg->rdesc.ops->get_mode) {
init_data->constraints.valid_ops_mask
|= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS;
- init_data->constraints.valid_modes_mask
- |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+
+ if (regulator_type == RPM_REGULATOR_TYPE_BOB)
+ init_data->constraints.valid_modes_mask
+ = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL;
+ else
+ init_data->constraints.valid_modes_mask
+ |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
}
reg->rdesc.name = init_data->constraints.name;
diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c
index 484f0412addf..d469463a0b00 100644
--- a/drivers/regulator/spm-regulator.c
+++ b/drivers/regulator/spm-regulator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -49,6 +49,7 @@ enum qpnp_regulator_uniq_type {
QPNP_TYPE_HF,
QPNP_TYPE_FTS2,
QPNP_TYPE_FTS2p5,
+ QPNP_TYPE_FTS426,
QPNP_TYPE_ULT_HF,
};
@@ -56,6 +57,7 @@ enum qpnp_regulator_type {
QPNP_HF_TYPE = 0x03,
QPNP_FTS2_TYPE = 0x1C,
QPNP_FTS2p5_TYPE = 0x1C,
+ QPNP_FTS426_TYPE = 0x1C,
QPNP_ULT_HF_TYPE = 0x22,
};
@@ -63,15 +65,22 @@ enum qpnp_regulator_subtype {
QPNP_FTS2_SUBTYPE = 0x08,
QPNP_HF_SUBTYPE = 0x08,
QPNP_FTS2p5_SUBTYPE = 0x09,
+ QPNP_FTS426_SUBTYPE = 0x0A,
QPNP_ULT_HF_SUBTYPE = 0x0D,
};
+enum qpnp_logical_mode {
+ QPNP_LOGICAL_MODE_AUTO,
+ QPNP_LOGICAL_MODE_PWM,
+};
+
static const struct voltage_range fts2_range0 = {0, 350000, 1275000, 5000};
static const struct voltage_range fts2_range1 = {0, 700000, 2040000, 10000};
static const struct voltage_range fts2p5_range0
= { 80000, 350000, 1355000, 5000};
static const struct voltage_range fts2p5_range1
= {160000, 700000, 2200000, 10000};
+static const struct voltage_range fts426_range = {0, 320000, 1352000, 4000};
static const struct voltage_range ult_hf_range0 = {375000, 375000, 1562500,
12500};
static const struct voltage_range ult_hf_range1 = {750000, 750000, 1525000,
@@ -86,17 +95,45 @@ 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
+#define QPNP_FTS426_REG_VOLTAGE_UB 0x41
+#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
+#define QPNP_FTS426_MODE_AUTO 0x06
#define QPNP_SMPS_STEP_CTRL_STEP_MASK 0x18
#define QPNP_SMPS_STEP_CTRL_STEP_SHIFT 3
#define QPNP_SMPS_STEP_CTRL_DELAY_MASK 0x07
#define QPNP_SMPS_STEP_CTRL_DELAY_SHIFT 0
+#define QPNP_FTS426_STEP_CTRL_DELAY_MASK 0x03
+#define QPNP_FTS426_STEP_CTRL_DELAY_SHIFT 0
/* Clock rate in kHz of the FTS2 regulator reference clock. */
#define QPNP_SMPS_CLOCK_RATE 19200
+#define QPNP_FTS426_CLOCK_RATE 4800
/* Time to delay in us to ensure that a mode change has completed. */
#define QPNP_FTS2_MODE_CHANGE_DELAY 50
@@ -107,6 +144,7 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000,
/* Minimum voltage stepper delay for each step. */
#define QPNP_FTS2_STEP_DELAY 8
#define QPNP_HF_STEP_DELAY 20
+#define QPNP_FTS426_STEP_DELAY 2
/* Arbitrarily large max step size used to avoid possible numerical overflow */
#define SPM_REGULATOR_MAX_STEP_UV 10000000
@@ -117,6 +155,8 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000,
*/
#define QPNP_FTS2_STEP_MARGIN_NUM 4
#define QPNP_FTS2_STEP_MARGIN_DEN 5
+#define QPNP_FTS426_STEP_MARGIN_NUM 10
+#define QPNP_FTS426_STEP_MARGIN_DEN 11
/*
* Settling delay for FTS2.5
@@ -140,8 +180,8 @@ struct spm_vreg {
u32 max_step_uV;
bool online;
u16 spmi_base_addr;
- u8 init_mode;
- u8 mode;
+ enum qpnp_logical_mode init_mode;
+ enum qpnp_logical_mode mode;
int step_rate;
enum qpnp_regulator_uniq_type regulator_type;
u32 cpu_num;
@@ -163,6 +203,9 @@ static int spm_regulator_uv_to_vlevel(struct spm_vreg *vreg, int uV)
{
int vlevel;
+ if (vreg->regulator_type == QPNP_TYPE_FTS426)
+ return roundup(uV, vreg->range->step_uV) / 1000;
+
vlevel = DIV_ROUND_UP(uV - vreg->range->min_uV, vreg->range->step_uV);
/* Fix VSET for ULT HF Buck */
@@ -177,6 +220,8 @@ static int spm_regulator_uv_to_vlevel(struct spm_vreg *vreg, int uV)
static int spm_regulator_vlevel_to_uv(struct spm_vreg *vreg, int vlevel)
{
+ if (vreg->regulator_type == QPNP_TYPE_FTS426)
+ return vlevel * 1000;
/*
* Calculate ULT HF buck VSET based on range:
* In case of range 0: VSET is a 7 bit value.
@@ -204,32 +249,91 @@ static unsigned spm_regulator_vlevel_to_selector(struct spm_vreg *vreg,
static int qpnp_smps_read_voltage(struct spm_vreg *vreg)
{
int rc;
- u8 reg = 0;
- uint val;
+ u8 val[2] = {0};
- rc = regmap_read(vreg->regmap,
- vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT,
- &val);
- if (rc) {
- dev_err(&vreg->pdev->dev,
- "%s: could not read voltage setpoint register, rc=%d\n",
- __func__, rc);
- return rc;
+ if (vreg->regulator_type == QPNP_TYPE_FTS426) {
+ rc = regmap_bulk_read(vreg->regmap,
+ vreg->spmi_base_addr + QPNP_FTS426_REG_VOLTAGE_VALID_LB,
+ val, 2);
+ if (rc) {
+ dev_err(&vreg->pdev->dev, "%s: could not read voltage setpoint registers, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ vreg->last_set_vlevel = ((unsigned)val[1] << 8) | val[0];
+ } else {
+ rc = regmap_bulk_read(vreg->regmap,
+ vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT,
+ val, 1);
+ if (rc) {
+ dev_err(&vreg->pdev->dev, "%s: could not read voltage setpoint register, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ vreg->last_set_vlevel = val[0];
}
- reg = (u8)val;
- vreg->last_set_vlevel = reg;
- vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, reg);
+ vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg,
+ vreg->last_set_vlevel);
+ return rc;
+}
+
+static int qpnp_smps_write_voltage(struct spm_vreg *vreg, unsigned vlevel)
+{
+ int rc = 0;
+ u8 reg[2];
+
+ /* Set voltage control registers via SPMI. */
+ reg[0] = vlevel & 0xFF;
+ reg[1] = (vlevel >> 8) & 0xFF;
+
+ if (vreg->regulator_type == QPNP_TYPE_FTS426) {
+ rc = regmap_bulk_write(vreg->regmap,
+ vreg->spmi_base_addr + QPNP_FTS426_REG_VOLTAGE_LB,
+ reg, 2);
+ } else {
+ rc = regmap_write(vreg->regmap,
+ vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT,
+ reg[0]);
+ }
+
+ if (rc)
+ pr_err("%s: regmap_write failed, rc=%d\n",
+ vreg->rdesc.name, rc);
return rc;
}
+static inline enum qpnp_logical_mode qpnp_regval_to_mode(struct spm_vreg *vreg,
+ u8 regval)
+{
+ if (vreg->regulator_type == QPNP_TYPE_FTS426)
+ return (regval == QPNP_FTS426_MODE_PWM)
+ ? QPNP_LOGICAL_MODE_PWM : QPNP_LOGICAL_MODE_AUTO;
+ else
+ return (regval & QPNP_SMPS_MODE_PWM)
+ ? QPNP_LOGICAL_MODE_PWM : QPNP_LOGICAL_MODE_AUTO;
+}
+
+static inline u8 qpnp_mode_to_regval(struct spm_vreg *vreg,
+ enum qpnp_logical_mode mode)
+{
+ if (vreg->regulator_type == QPNP_TYPE_FTS426)
+ return (mode == QPNP_LOGICAL_MODE_PWM)
+ ? QPNP_FTS426_MODE_PWM : QPNP_FTS426_MODE_AUTO;
+ else
+ return (mode == QPNP_LOGICAL_MODE_PWM)
+ ? QPNP_SMPS_MODE_PWM : QPNP_SMPS_MODE_AUTO;
+}
+
static int qpnp_smps_set_mode(struct spm_vreg *vreg, u8 mode)
{
int rc;
rc = regmap_write(vreg->regmap,
- vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, mode);
+ vreg->spmi_base_addr + QPNP_SMPS_REG_MODE,
+ qpnp_mode_to_regval(vreg, mode));
if (rc)
dev_err(&vreg->pdev->dev,
"%s: could not write to mode register, rc=%d\n",
@@ -275,7 +379,6 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV)
bool spm_failed = false;
int rc = 0;
u32 slew_delay;
- u8 reg;
if (likely(!vreg->bypass_spm)) {
/* Set voltage control register via SPM. */
@@ -288,13 +391,9 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV)
}
if (unlikely(vreg->bypass_spm || spm_failed)) {
- /* Set voltage control register via SPMI. */
- reg = vlevel;
- rc = regmap_write(vreg->regmap,
- vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT,
- reg);
+ rc = qpnp_smps_write_voltage(vreg, vlevel);
if (rc) {
- pr_err("%s: regmap_write failed, rc=%d\n",
+ pr_err("%s: voltage write failed, rc=%d\n",
vreg->rdesc.name, rc);
return rc;
}
@@ -351,12 +450,12 @@ static int _spm_regulator_set_voltage(struct regulator_dev *rdev)
return 0;
pwm_required = (vreg->regulator_type == QPNP_TYPE_FTS2)
- && !(vreg->init_mode & QPNP_SMPS_MODE_PWM)
+ && (vreg->init_mode != QPNP_LOGICAL_MODE_PWM)
&& vreg->uV > vreg->last_set_uV;
if (pwm_required) {
/* Switch to PWM mode so that voltage ramping is fast. */
- rc = qpnp_smps_set_mode(vreg, QPNP_SMPS_MODE_PWM);
+ rc = qpnp_smps_set_mode(vreg, QPNP_LOGICAL_MODE_PWM);
if (rc)
return rc;
}
@@ -375,7 +474,7 @@ static int _spm_regulator_set_voltage(struct regulator_dev *rdev)
/* Wait for mode transition to complete. */
udelay(QPNP_FTS2_MODE_CHANGE_DELAY - QPNP_SPMI_WRITE_MIN_DELAY);
/* Switch to AUTO mode so that power consumption is lowered. */
- rc = qpnp_smps_set_mode(vreg, QPNP_SMPS_MODE_AUTO);
+ rc = qpnp_smps_set_mode(vreg, QPNP_LOGICAL_MODE_AUTO);
if (rc)
return rc;
}
@@ -466,7 +565,7 @@ static unsigned int spm_regulator_get_mode(struct regulator_dev *rdev)
{
struct spm_vreg *vreg = rdev_get_drvdata(rdev);
- return vreg->mode == QPNP_SMPS_MODE_PWM
+ return vreg->mode == QPNP_LOGICAL_MODE_PWM
? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
}
@@ -480,8 +579,8 @@ static int spm_regulator_set_mode(struct regulator_dev *rdev, unsigned int mode)
* in the case that qcom,mode has been specified as "pwm" in device
* tree.
*/
- vreg->mode
- = mode == REGULATOR_MODE_NORMAL ? QPNP_SMPS_MODE_PWM : vreg->init_mode;
+ vreg->mode = (mode == REGULATOR_MODE_NORMAL) ? QPNP_LOGICAL_MODE_PWM
+ : vreg->init_mode;
return qpnp_smps_set_mode(vreg, vreg->mode);
}
@@ -538,7 +637,7 @@ static int spm_regulator_avs_set_voltage(struct regulator_dev *rdev, int min_uV,
return -EINVAL;
}
- vlevel_max = (uV - range->min_uV) / range->step_uV;
+ vlevel_max = spm_regulator_uv_to_vlevel(vreg, uV);
avs_max_uV = spm_regulator_vlevel_to_uv(vreg, vlevel_max);
if (avs_max_uV < min_uV) {
@@ -646,6 +745,9 @@ static int qpnp_smps_check_type(struct spm_vreg *vreg)
} else if (type[0] == QPNP_FTS2p5_TYPE
&& type[1] == QPNP_FTS2p5_SUBTYPE) {
vreg->regulator_type = QPNP_TYPE_FTS2p5;
+ } else if (type[0] == QPNP_FTS426_TYPE
+ && type[1] == QPNP_FTS426_SUBTYPE) {
+ vreg->regulator_type = QPNP_TYPE_FTS426;
} else if (type[0] == QPNP_ULT_HF_TYPE
&& type[1] == QPNP_ULT_HF_SUBTYPE) {
vreg->regulator_type = QPNP_TYPE_ULT_HF;
@@ -750,10 +852,10 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg)
&mode_name);
if (!rc) {
if (strcmp("pwm", mode_name) == 0) {
- vreg->init_mode = QPNP_SMPS_MODE_PWM;
+ vreg->init_mode = QPNP_LOGICAL_MODE_PWM;
} else if ((strcmp("auto", mode_name) == 0) &&
(vreg->regulator_type != QPNP_TYPE_ULT_HF)) {
- vreg->init_mode = QPNP_SMPS_MODE_AUTO;
+ vreg->init_mode = QPNP_LOGICAL_MODE_AUTO;
} else {
dev_err(&vreg->pdev->dev,
"%s: unknown regulator mode: %s\n",
@@ -761,13 +863,9 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg)
return -EINVAL;
}
- rc = regmap_write(vreg->regmap,
- vreg->spmi_base_addr + QPNP_SMPS_REG_MODE,
- *&vreg->init_mode);
+ rc = qpnp_smps_set_mode(vreg, vreg->init_mode);
if (rc)
- dev_err(&vreg->pdev->dev,
- "%s: could not write mode register, rc=%d\n",
- __func__, rc);
+ return rc;
} else {
rc = regmap_read(vreg->regmap,
vreg->spmi_base_addr + QPNP_SMPS_REG_MODE,
@@ -776,7 +874,7 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg)
dev_err(&vreg->pdev->dev,
"%s: could not read mode register, rc=%d\n",
__func__, rc);
- vreg->init_mode = (u8)val;
+ vreg->init_mode = qpnp_regval_to_mode(vreg, val);
}
vreg->mode = vreg->init_mode;
@@ -801,26 +899,41 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg)
}
reg = (u8)val;
- /* ULT buck does not support steps */
- if (vreg->regulator_type != QPNP_TYPE_ULT_HF)
+ /* ULT and FTS426 bucks do not support steps */
+ if (vreg->regulator_type != QPNP_TYPE_ULT_HF && vreg->regulator_type !=
+ QPNP_TYPE_FTS426)
step = (reg & QPNP_SMPS_STEP_CTRL_STEP_MASK)
>> QPNP_SMPS_STEP_CTRL_STEP_SHIFT;
- delay = (reg & QPNP_SMPS_STEP_CTRL_DELAY_MASK)
- >> QPNP_SMPS_STEP_CTRL_DELAY_SHIFT;
+ if (vreg->regulator_type == QPNP_TYPE_FTS426) {
+ delay = (reg & QPNP_FTS426_STEP_CTRL_DELAY_MASK)
+ >> QPNP_FTS426_STEP_CTRL_DELAY_SHIFT;
- /* step_rate has units of uV/us. */
- vreg->step_rate = QPNP_SMPS_CLOCK_RATE * vreg->range->step_uV
- * (1 << step);
+ /* step_rate has units of uV/us. */
+ vreg->step_rate = QPNP_FTS426_CLOCK_RATE * vreg->range->step_uV;
+ } else {
+ delay = (reg & QPNP_SMPS_STEP_CTRL_DELAY_MASK)
+ >> QPNP_SMPS_STEP_CTRL_DELAY_SHIFT;
+
+ /* step_rate has units of uV/us. */
+ vreg->step_rate = QPNP_SMPS_CLOCK_RATE * vreg->range->step_uV
+ * (1 << step);
+ }
if ((vreg->regulator_type == QPNP_TYPE_ULT_HF)
|| (vreg->regulator_type == QPNP_TYPE_HF))
vreg->step_rate /= 1000 * (QPNP_HF_STEP_DELAY << delay);
+ else if (vreg->regulator_type == QPNP_TYPE_FTS426)
+ vreg->step_rate /= 1000 * (QPNP_FTS426_STEP_DELAY << delay);
else
vreg->step_rate /= 1000 * (QPNP_FTS2_STEP_DELAY << delay);
- vreg->step_rate = vreg->step_rate * QPNP_FTS2_STEP_MARGIN_NUM
- / QPNP_FTS2_STEP_MARGIN_DEN;
+ if (vreg->regulator_type == QPNP_TYPE_FTS426)
+ vreg->step_rate = vreg->step_rate * QPNP_FTS426_STEP_MARGIN_NUM
+ / QPNP_FTS426_STEP_MARGIN_DEN;
+ else
+ vreg->step_rate = vreg->step_rate * QPNP_FTS2_STEP_MARGIN_NUM
+ / QPNP_FTS2_STEP_MARGIN_DEN;
/* Ensure that the stepping rate is greater than 0. */
vreg->step_rate = max(vreg->step_rate, 1);
@@ -828,10 +941,93 @@ 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
- || vreg->range == &ult_hf_range0 || vreg->range == &hf_range0;
+ || vreg->range == &ult_hf_range0 || vreg->range == &hf_range0
+ || vreg->range == &fts426_range;
}
/* Register a regulator to enable/disable AVS and set AVS min/max limits. */
@@ -969,6 +1165,8 @@ static int spm_regulator_probe(struct platform_device *pdev)
rc = qpnp_smps_init_range(vreg, &fts2_range0, &fts2_range1);
else if (vreg->regulator_type == QPNP_TYPE_FTS2p5)
rc = qpnp_smps_init_range(vreg, &fts2p5_range0, &fts2p5_range1);
+ else if (vreg->regulator_type == QPNP_TYPE_FTS426)
+ vreg->range = &fts426_range;
else if (vreg->regulator_type == QPNP_TYPE_HF)
rc = qpnp_smps_init_range(vreg, &hf_range0, &hf_range1);
else if (vreg->regulator_type == QPNP_TYPE_ULT_HF)
@@ -1006,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;
@@ -1050,8 +1255,8 @@ static int spm_regulator_probe(struct platform_device *pdev)
vreg->rdesc.name,
spm_regulator_using_range0(vreg) ? "LV" : "MV",
vreg->uV,
- vreg->init_mode & QPNP_SMPS_MODE_PWM ? "PWM" :
- (vreg->init_mode & QPNP_SMPS_MODE_AUTO ? "AUTO" : "PFM"),
+ vreg->init_mode == QPNP_LOGICAL_MODE_PWM ? "PWM" :
+ (vreg->init_mode == QPNP_LOGICAL_MODE_AUTO ? "AUTO" : "PFM"),
vreg->step_rate);
return rc;
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/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 1766a20ebcb1..741f3ee81cfe 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -717,6 +717,7 @@ enum qeth_discipline_id {
};
struct qeth_discipline {
+ const struct device_type *devtype;
void (*start_poll)(struct ccw_device *, int, unsigned long);
qdio_handler_t *input_handler;
qdio_handler_t *output_handler;
@@ -881,6 +882,9 @@ extern struct qeth_discipline qeth_l2_discipline;
extern struct qeth_discipline qeth_l3_discipline;
extern const struct attribute_group *qeth_generic_attr_groups[];
extern const struct attribute_group *qeth_osn_attr_groups[];
+extern const struct attribute_group qeth_device_attr_group;
+extern const struct attribute_group qeth_device_blkt_group;
+extern const struct device_type qeth_generic_devtype;
extern struct workqueue_struct *qeth_wq;
int qeth_card_hw_is_reachable(struct qeth_card *);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 31ac53fa5cee..d10bf3da8e5f 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -5449,10 +5449,12 @@ void qeth_core_free_discipline(struct qeth_card *card)
card->discipline = NULL;
}
-static const struct device_type qeth_generic_devtype = {
+const struct device_type qeth_generic_devtype = {
.name = "qeth_generic",
.groups = qeth_generic_attr_groups,
};
+EXPORT_SYMBOL_GPL(qeth_generic_devtype);
+
static const struct device_type qeth_osn_devtype = {
.name = "qeth_osn",
.groups = qeth_osn_attr_groups,
@@ -5578,23 +5580,22 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
goto err_card;
}
- if (card->info.type == QETH_CARD_TYPE_OSN)
- gdev->dev.type = &qeth_osn_devtype;
- else
- gdev->dev.type = &qeth_generic_devtype;
-
switch (card->info.type) {
case QETH_CARD_TYPE_OSN:
case QETH_CARD_TYPE_OSM:
rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2);
if (rc)
goto err_card;
+
+ gdev->dev.type = (card->info.type != QETH_CARD_TYPE_OSN)
+ ? card->discipline->devtype
+ : &qeth_osn_devtype;
rc = card->discipline->setup(card->gdev);
if (rc)
goto err_disc;
- case QETH_CARD_TYPE_OSD:
- case QETH_CARD_TYPE_OSX:
+ break;
default:
+ gdev->dev.type = &qeth_generic_devtype;
break;
}
@@ -5650,8 +5651,10 @@ static int qeth_core_set_online(struct ccwgroup_device *gdev)
if (rc)
goto err;
rc = card->discipline->setup(card->gdev);
- if (rc)
+ if (rc) {
+ qeth_core_free_discipline(card);
goto err;
+ }
}
rc = card->discipline->set_online(gdev);
err:
diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c
index e6e5b9671bf2..fa844b0ff847 100644
--- a/drivers/s390/net/qeth_core_sys.c
+++ b/drivers/s390/net/qeth_core_sys.c
@@ -409,12 +409,16 @@ static ssize_t qeth_dev_layer2_store(struct device *dev,
if (card->options.layer2 == newdis)
goto out;
- else {
- card->info.mac_bits = 0;
- if (card->discipline) {
- card->discipline->remove(card->gdev);
- qeth_core_free_discipline(card);
- }
+ if (card->info.type == QETH_CARD_TYPE_OSM) {
+ /* fixed layer, can't switch */
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ card->info.mac_bits = 0;
+ if (card->discipline) {
+ card->discipline->remove(card->gdev);
+ qeth_core_free_discipline(card);
}
rc = qeth_core_load_discipline(card, newdis);
@@ -422,6 +426,8 @@ static ssize_t qeth_dev_layer2_store(struct device *dev,
goto out;
rc = card->discipline->setup(card->gdev);
+ if (rc)
+ qeth_core_free_discipline(card);
out:
mutex_unlock(&card->discipline_mutex);
return rc ? rc : count;
@@ -699,10 +705,11 @@ static struct attribute *qeth_blkt_device_attrs[] = {
&dev_attr_inter_jumbo.attr,
NULL,
};
-static struct attribute_group qeth_device_blkt_group = {
+const struct attribute_group qeth_device_blkt_group = {
.name = "blkt",
.attrs = qeth_blkt_device_attrs,
};
+EXPORT_SYMBOL_GPL(qeth_device_blkt_group);
static struct attribute *qeth_device_attrs[] = {
&dev_attr_state.attr,
@@ -722,9 +729,10 @@ static struct attribute *qeth_device_attrs[] = {
&dev_attr_switch_attrs.attr,
NULL,
};
-static struct attribute_group qeth_device_attr_group = {
+const struct attribute_group qeth_device_attr_group = {
.attrs = qeth_device_attrs,
};
+EXPORT_SYMBOL_GPL(qeth_device_attr_group);
const struct attribute_group *qeth_generic_attr_groups[] = {
&qeth_device_attr_group,
diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h
index 0767556404bd..eb87bf97d38a 100644
--- a/drivers/s390/net/qeth_l2.h
+++ b/drivers/s390/net/qeth_l2.h
@@ -8,6 +8,8 @@
#include "qeth_core.h"
+extern const struct attribute_group *qeth_l2_attr_groups[];
+
int qeth_l2_create_device_attributes(struct device *);
void qeth_l2_remove_device_attributes(struct device *);
void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card);
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index df036b872b05..bf1e0e39334d 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1027,11 +1027,21 @@ static int qeth_l2_stop(struct net_device *dev)
return 0;
}
+static const struct device_type qeth_l2_devtype = {
+ .name = "qeth_layer2",
+ .groups = qeth_l2_attr_groups,
+};
+
static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
{
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc;
- qeth_l2_create_device_attributes(&gdev->dev);
+ if (gdev->dev.type == &qeth_generic_devtype) {
+ rc = qeth_l2_create_device_attributes(&gdev->dev);
+ if (rc)
+ return rc;
+ }
INIT_LIST_HEAD(&card->vid_list);
hash_init(card->mac_htable);
card->options.layer2 = 1;
@@ -1043,7 +1053,8 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
{
struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
- qeth_l2_remove_device_attributes(&cgdev->dev);
+ if (cgdev->dev.type == &qeth_generic_devtype)
+ qeth_l2_remove_device_attributes(&cgdev->dev);
qeth_set_allowed_threads(card, 0, 1);
wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
@@ -1101,7 +1112,6 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
case QETH_CARD_TYPE_OSN:
card->dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN,
ether_setup);
- card->dev->flags |= IFF_NOARP;
break;
default:
card->dev = alloc_etherdev(0);
@@ -1114,9 +1124,12 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
card->dev->mtu = card->info.initial_mtu;
card->dev->netdev_ops = &qeth_l2_netdev_ops;
- card->dev->ethtool_ops =
- (card->info.type != QETH_CARD_TYPE_OSN) ?
- &qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
+ if (card->info.type == QETH_CARD_TYPE_OSN) {
+ card->dev->ethtool_ops = &qeth_l2_osn_ops;
+ card->dev->flags |= IFF_NOARP;
+ } else {
+ card->dev->ethtool_ops = &qeth_l2_ethtool_ops;
+ }
card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) {
card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
@@ -1429,6 +1442,7 @@ static int qeth_l2_control_event(struct qeth_card *card,
}
struct qeth_discipline qeth_l2_discipline = {
+ .devtype = &qeth_l2_devtype,
.start_poll = qeth_qdio_start_poll,
.input_handler = (qdio_handler_t *) qeth_qdio_input_handler,
.output_handler = (qdio_handler_t *) qeth_qdio_output_handler,
diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c
index 692db49e3d2a..a48ed9e7e168 100644
--- a/drivers/s390/net/qeth_l2_sys.c
+++ b/drivers/s390/net/qeth_l2_sys.c
@@ -272,3 +272,11 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
} else
qeth_bridgeport_an_set(card, 0);
}
+
+const struct attribute_group *qeth_l2_attr_groups[] = {
+ &qeth_device_attr_group,
+ &qeth_device_blkt_group,
+ /* l2 specific, see l2_{create,remove}_device_attributes(): */
+ &qeth_l2_bridgeport_attr_group,
+ NULL,
+};
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index cc4d3c3d8cc5..285fe0b2c753 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -3227,8 +3227,11 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
static int qeth_l3_probe_device(struct ccwgroup_device *gdev)
{
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc;
- qeth_l3_create_device_attributes(&gdev->dev);
+ rc = qeth_l3_create_device_attributes(&gdev->dev);
+ if (rc)
+ return rc;
card->options.layer2 = 0;
card->info.hwtrap = 0;
return 0;
@@ -3519,6 +3522,7 @@ static int qeth_l3_control_event(struct qeth_card *card,
}
struct qeth_discipline qeth_l3_discipline = {
+ .devtype = &qeth_generic_devtype,
.start_poll = qeth_qdio_start_poll,
.input_handler = (qdio_handler_t *) qeth_qdio_input_handler,
.output_handler = (qdio_handler_t *) qeth_qdio_output_handler,
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 8a5fbdb45cfd..e333029e4b6c 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -4452,6 +4452,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
struct MPT3SAS_DEVICE *sas_device_priv_data;
u32 response_code = 0;
unsigned long flags;
+ unsigned int sector_sz;
mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
scmd = _scsih_scsi_lookup_get_clear(ioc, smid);
@@ -4510,6 +4511,20 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
}
xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
+
+ /* In case of bogus fw or device, we could end up having
+ * unaligned partial completion. We can force alignment here,
+ * then scsi-ml does not need to handle this misbehavior.
+ */
+ sector_sz = scmd->device->sector_size;
+ if (unlikely(scmd->request->cmd_type == REQ_TYPE_FS && sector_sz &&
+ xfer_cnt % sector_sz)) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "unaligned partial completion avoided (xfer_cnt=%u, sector_sz=%u)\n",
+ xfer_cnt, sector_sz);
+ xfer_cnt = round_down(xfer_cnt, sector_sz);
+ }
+
scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt);
if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 3588a56aabb4..5cbf20ab94aa 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2311,10 +2311,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (mem_only) {
if (pci_enable_device_mem(pdev))
- goto probe_out;
+ return ret;
} else {
if (pci_enable_device(pdev))
- goto probe_out;
+ return ret;
}
/* This may fail but that's ok */
@@ -2324,7 +2324,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (!ha) {
ql_log_pci(ql_log_fatal, pdev, 0x0009,
"Unable to allocate memory for ha.\n");
- goto probe_out;
+ goto disable_device;
}
ql_dbg_pci(ql_dbg_init, pdev, 0x000a,
"Memory allocated for ha=%p.\n", ha);
@@ -2923,7 +2923,7 @@ iospace_config_failed:
kfree(ha);
ha = NULL;
-probe_out:
+disable_device:
pci_disable_device(pdev);
return ret;
}
diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c
index 0c862639fa3e..84765b17086c 100644
--- a/drivers/scsi/ufs/ufs-qcom-ice.c
+++ b/drivers/scsi/ufs/ufs-qcom-ice.c
@@ -170,17 +170,15 @@ out:
static void ufs_qcom_ice_cfg_work(struct work_struct *work)
{
unsigned long flags;
- struct ice_data_setting ice_set;
struct ufs_qcom_host *qcom_host =
container_of(work, struct ufs_qcom_host, ice_cfg_work);
- struct request *req_pending = NULL;
if (!qcom_host->ice.vops->config_start)
return;
spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
- req_pending = qcom_host->req_pending;
- if (!req_pending) {
+ if (!qcom_host->req_pending) {
+ qcom_host->work_pending = false;
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
return;
}
@@ -189,24 +187,15 @@ static void ufs_qcom_ice_cfg_work(struct work_struct *work)
/*
* config_start is called again as previous attempt returned -EAGAIN,
* this call shall now take care of the necessary key setup.
- * 'ice_set' will not actually be used, instead the next call to
- * config_start() for this request, in the normal call flow, will
- * succeed as the key has now been setup.
*/
qcom_host->ice.vops->config_start(qcom_host->ice.pdev,
- qcom_host->req_pending, &ice_set, false);
+ qcom_host->req_pending, NULL, false);
spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
qcom_host->req_pending = NULL;
+ qcom_host->work_pending = false;
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
- /*
- * Resume with requests processing. We assume config_start has been
- * successful, but even if it wasn't we still must resume in order to
- * allow for the request to be retried.
- */
- ufshcd_scsi_unblock_requests(qcom_host->hba);
-
}
/**
@@ -285,18 +274,14 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host,
* requires a non-atomic context, this means we should
* call the function again from the worker thread to do
* the configuration. For this request the error will
- * propagate so it will be re-queued and until the
- * configuration is is completed we block further
- * request processing.
+ * propagate so it will be re-queued.
*/
if (err == -EAGAIN) {
dev_dbg(qcom_host->hba->dev,
"%s: scheduling task for ice setup\n",
__func__);
- if (!qcom_host->req_pending) {
- ufshcd_scsi_block_requests(
- qcom_host->hba);
+ if (!qcom_host->work_pending) {
qcom_host->req_pending = cmd->request;
if (!schedule_work(
@@ -307,10 +292,9 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host,
&qcom_host->ice_work_lock,
flags);
- ufshcd_scsi_unblock_requests(
- qcom_host->hba);
return err;
}
+ qcom_host->work_pending = true;
}
} else {
@@ -409,9 +393,7 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
* requires a non-atomic context, this means we should
* call the function again from the worker thread to do
* the configuration. For this request the error will
- * propagate so it will be re-queued and until the
- * configuration is is completed we block further
- * request processing.
+ * propagate so it will be re-queued.
*/
if (err == -EAGAIN) {
@@ -419,9 +401,8 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
"%s: scheduling task for ice setup\n",
__func__);
- if (!qcom_host->req_pending) {
- ufshcd_scsi_block_requests(
- qcom_host->hba);
+ if (!qcom_host->work_pending) {
+
qcom_host->req_pending = cmd->request;
if (!schedule_work(
&qcom_host->ice_cfg_work)) {
@@ -431,10 +412,9 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
&qcom_host->ice_work_lock,
flags);
- ufshcd_scsi_unblock_requests(
- qcom_host->hba);
return err;
}
+ qcom_host->work_pending = true;
}
} else {
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/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 12154b268132..460213536e14 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -374,6 +374,7 @@ struct ufs_qcom_host {
struct work_struct ice_cfg_work;
struct request *req_pending;
struct ufs_vreg *vddp_ref_clk;
+ bool work_pending;
};
static inline u32
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a90c51a113d2..a2f2cc0c2c51 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
@@ -651,7 +653,7 @@ static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba,
unsigned int tag, const char *str)
{
struct ufshcd_lrb *lrbp;
- char *cmd_type;
+ char *cmd_type = NULL;
u8 opcode = 0;
u8 cmd_id = 0, idn = 0;
sector_t lba = -1;
@@ -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/Kconfig b/drivers/soc/qcom/Kconfig
index ea008ffbc856..0c4414d27eeb 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -468,6 +468,7 @@ config MINIDUMP_MAX_ENTRIES
config ICNSS
tristate "Platform driver for Q6 integrated connectivity"
+ select CNSS_UTILS
---help---
This module adds support for Q6 integrated WLAN connectivity
subsystem. This module is responsible for communicating WLAN on/off
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 561b9074f2ee..e0d9f68ceef9 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -30,7 +30,6 @@
#include "glink_private.h"
#include "glink_xprt_if.h"
-#define GLINK_CTX_CANARY 0x58544324 /* "$CTX" */
/* Number of internal IPC Logging log pages */
#define NUM_LOG_PAGES 10
#define GLINK_PM_QOS_HOLDOFF_MS 10
@@ -40,7 +39,6 @@
#define GLINK_KTHREAD_PRIO 1
-static rwlock_t magic_lock;
/**
* struct glink_qos_priority_bin - Packet Scheduler's priority bucket
* @max_rate_kBps: Maximum rate supported by the priority bucket.
@@ -316,7 +314,6 @@ struct channel_ctx {
uint32_t rt_vote_on;
uint32_t rt_vote_off;
- uint32_t magic_number;
};
static struct glink_core_if core_impl;
@@ -447,33 +444,15 @@ static void glink_core_deinit_xprt_qos_cfg(
static int glink_get_ch_ctx(struct channel_ctx *ctx)
{
- unsigned long flags;
-
if (!ctx)
return -EINVAL;
- read_lock_irqsave(&magic_lock, flags);
- if (ctx->magic_number != GLINK_CTX_CANARY) {
- read_unlock_irqrestore(&magic_lock, flags);
- return -EINVAL;
- }
rwref_get(&ctx->ch_state_lhb2);
- read_unlock_irqrestore(&magic_lock, flags);
return 0;
}
-static int glink_put_ch_ctx(struct channel_ctx *ctx, bool update_magic)
+static void glink_put_ch_ctx(struct channel_ctx *ctx)
{
- unsigned long flags;
-
- if (!update_magic) {
- rwref_put(&ctx->ch_state_lhb2);
- return 0;
- }
- write_lock_irqsave(&magic_lock, flags);
- ctx->magic_number = 0;
rwref_put(&ctx->ch_state_lhb2);
- write_unlock_irqrestore(&magic_lock, flags);
- return 0;
}
/**
@@ -1935,13 +1914,14 @@ check_ctx:
kfree(flcid);
}
+ ctx->transport_ptr = xprt_ctx;
+ rwref_get(&ctx->ch_state_lhb2);
list_add_tail(&ctx->port_list_node, &xprt_ctx->channels);
GLINK_INFO_PERF_CH_XPRT(ctx, xprt_ctx,
"%s: local:GLINK_CHANNEL_CLOSED\n",
__func__);
}
- rwref_get(&ctx->ch_state_lhb2);
spin_unlock_irqrestore(&xprt_ctx->xprt_ctx_lock_lhb1, flags);
rwref_write_put(&xprt_ctx->xprt_state_lhb0);
mutex_lock(&xprt_ctx->xprt_dbgfs_lock_lhb4);
@@ -2623,7 +2603,6 @@ void *glink_open(const struct glink_open_config *cfg)
ctx->notify_tx_abort = cfg->notify_tx_abort;
ctx->notify_rx_tracer_pkt = cfg->notify_rx_tracer_pkt;
ctx->notify_remote_rx_intent = cfg->notify_remote_rx_intent;
- ctx->magic_number = GLINK_CTX_CANARY;
if (!ctx->notify_rx_intent_req)
ctx->notify_rx_intent_req = glink_dummy_notify_rx_intent_req;
@@ -2640,7 +2619,6 @@ void *glink_open(const struct glink_open_config *cfg)
ctx->local_xprt_req = best_id;
ctx->no_migrate = cfg->transport &&
!(cfg->options & GLINK_OPT_INITIAL_XPORT);
- ctx->transport_ptr = transport_ptr;
ctx->local_open_state = GLINK_CHANNEL_OPENING;
GLINK_INFO_PERF_CH(ctx,
"%s: local:GLINK_CHANNEL_CLOSED->GLINK_CHANNEL_OPENING\n",
@@ -2764,13 +2742,13 @@ int glink_close(void *handle)
GLINK_INFO_CH(ctx, "%s: Closing channel, ctx: %p\n", __func__, ctx);
if (ctx->local_open_state == GLINK_CHANNEL_CLOSED) {
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return 0;
}
if (ctx->local_open_state == GLINK_CHANNEL_CLOSING) {
/* close already pending */
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
@@ -2835,7 +2813,7 @@ relock: xprt_ctx = ctx->transport_ptr;
rwref_put(&ctx->ch_state_lhb2);
rwref_read_put(&xprt_ctx->xprt_state_lhb0);
- glink_put_ch_ctx(ctx, true);
+ glink_put_ch_ctx(ctx);
return ret;
}
EXPORT_SYMBOL(glink_close);
@@ -2884,7 +2862,7 @@ static int glink_tx_common(void *handle, void *pkt_priv,
struct channel_ctx *ctx = (struct channel_ctx *)handle;
uint32_t riid;
int ret = 0;
- struct glink_core_tx_pkt *tx_info;
+ struct glink_core_tx_pkt *tx_info = NULL;
size_t intent_size;
bool is_atomic =
tx_flags & (GLINK_TX_SINGLE_THREADED | GLINK_TX_ATOMIC);
@@ -2899,6 +2877,13 @@ static int glink_tx_common(void *handle, void *pkt_priv,
return ret;
rwref_read_get_atomic(&ctx->ch_state_lhb2, is_atomic);
+ tx_info = kzalloc(sizeof(struct glink_core_tx_pkt),
+ is_atomic ? GFP_ATOMIC : GFP_KERNEL);
+ if (!tx_info) {
+ GLINK_ERR_CH(ctx, "%s: No memory for allocation\n", __func__);
+ ret = -ENOMEM;
+ goto glink_tx_common_err;
+ }
if (!(vbuf_provider || pbuf_provider)) {
ret = -EINVAL;
goto glink_tx_common_err;
@@ -3018,14 +3003,7 @@ static int glink_tx_common(void *handle, void *pkt_priv,
GLINK_INFO_PERF_CH(ctx, "%s: R[%u]:%zu data[%p], size[%zu]. TID %u\n",
__func__, riid, intent_size,
data ? data : iovec, size, current->pid);
- tx_info = kzalloc(sizeof(struct glink_core_tx_pkt),
- is_atomic ? GFP_ATOMIC : GFP_KERNEL);
- if (!tx_info) {
- GLINK_ERR_CH(ctx, "%s: No memory for allocation\n", __func__);
- ch_push_remote_rx_intent(ctx, intent_size, riid, cookie);
- ret = -ENOMEM;
- goto glink_tx_common_err;
- }
+
rwref_lock_init(&tx_info->pkt_ref, glink_tx_pkt_release);
INIT_LIST_HEAD(&tx_info->list_done);
INIT_LIST_HEAD(&tx_info->list_node);
@@ -3050,10 +3028,15 @@ static int glink_tx_common(void *handle, void *pkt_priv,
else
xprt_schedule_tx(ctx->transport_ptr, ctx, tx_info);
+ rwref_read_put(&ctx->ch_state_lhb2);
+ glink_put_ch_ctx(ctx);
+ return ret;
+
glink_tx_common_err:
rwref_read_put(&ctx->ch_state_lhb2);
glink_tx_common_err_2:
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
+ kfree(tx_info);
return ret;
}
@@ -3102,7 +3085,7 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size)
/* Can only queue rx intents if channel is fully opened */
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
@@ -3111,14 +3094,14 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size)
GLINK_ERR_CH(ctx,
"%s: Intent pointer allocation failed size[%zu]\n",
__func__, size);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -ENOMEM;
}
GLINK_DBG_CH(ctx, "%s: L[%u]:%zu\n", __func__, intent_ptr->id,
intent_ptr->intent_size);
if (ctx->transport_ptr->capabilities & GCAP_INTENTLESS) {
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
@@ -3128,7 +3111,7 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size)
if (ret)
/* unable to transmit, dequeue intent */
ch_remove_local_rx_intent(ctx, intent_ptr->id);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
EXPORT_SYMBOL(glink_queue_rx_intent);
@@ -3160,12 +3143,12 @@ bool glink_rx_intent_exists(void *handle, size_t size)
if (size <= intent->intent_size) {
spin_unlock_irqrestore(
&ctx->local_rx_intent_lst_lock_lhc1, flags);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return true;
}
}
spin_unlock_irqrestore(&ctx->local_rx_intent_lst_lock_lhc1, flags);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return false;
}
EXPORT_SYMBOL(glink_rx_intent_exists);
@@ -3194,7 +3177,7 @@ int glink_rx_done(void *handle, const void *ptr, bool reuse)
if (IS_ERR_OR_NULL(liid_ptr)) {
/* invalid pointer */
GLINK_ERR_CH(ctx, "%s: Invalid pointer %p\n", __func__, ptr);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EINVAL;
}
@@ -3220,7 +3203,7 @@ int glink_rx_done(void *handle, const void *ptr, bool reuse)
/* send rx done */
ctx->transport_ptr->ops->tx_cmd_local_rx_done(ctx->transport_ptr->ops,
ctx->lcid, id, reuse);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
EXPORT_SYMBOL(glink_rx_done);
@@ -3274,7 +3257,7 @@ int glink_sigs_set(void *handle, uint32_t sigs)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
@@ -3284,7 +3267,7 @@ int glink_sigs_set(void *handle, uint32_t sigs)
ctx->lcid, ctx->lsigs);
GLINK_INFO_CH(ctx, "%s: Sent SIGNAL SET command\n", __func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
EXPORT_SYMBOL(glink_sigs_set);
@@ -3310,12 +3293,12 @@ int glink_sigs_local_get(void *handle, uint32_t *sigs)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
*sigs = ctx->lsigs;
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return 0;
}
EXPORT_SYMBOL(glink_sigs_local_get);
@@ -3342,12 +3325,12 @@ int glink_sigs_remote_get(void *handle, uint32_t *sigs)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
*sigs = ctx->rsigs;
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return 0;
}
EXPORT_SYMBOL(glink_sigs_remote_get);
@@ -3452,7 +3435,7 @@ int glink_qos_latency(void *handle, unsigned long latency_us, size_t pkt_size)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
@@ -3462,7 +3445,7 @@ int glink_qos_latency(void *handle, unsigned long latency_us, size_t pkt_size)
if (ret < 0)
GLINK_ERR_CH(ctx, "%s: QoS %lu:%zu cannot be met\n",
__func__, latency_us, pkt_size);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
EXPORT_SYMBOL(glink_qos_latency);
@@ -3486,12 +3469,12 @@ int glink_qos_cancel(void *handle)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
ret = glink_qos_reset_priority(ctx);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
EXPORT_SYMBOL(glink_qos_cancel);
@@ -3518,7 +3501,7 @@ int glink_qos_start(void *handle)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
@@ -3527,7 +3510,7 @@ int glink_qos_start(void *handle)
ret = glink_qos_add_ch_tx_intent(ctx);
spin_unlock(&ctx->tx_lists_lock_lhc3);
spin_unlock_irqrestore(&ctx->transport_ptr->tx_ready_lock_lhb3, flags);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
EXPORT_SYMBOL(glink_qos_start);
@@ -3555,11 +3538,11 @@ unsigned long glink_qos_get_ramp_time(void *handle, size_t pkt_size)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return (unsigned long)-EBUSY;
}
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ctx->transport_ptr->ops->get_power_vote_ramp_time(
ctx->transport_ptr->ops,
glink_prio_to_power_state(ctx->transport_ptr,
@@ -3585,13 +3568,13 @@ int glink_start_rx_rt(void *handle)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
ret = ctx->transport_ptr->ops->rx_rt_vote(ctx->transport_ptr->ops);
ctx->rt_vote_on++;
GLINK_INFO_CH(ctx, "%s: Voting RX Realtime Thread %d", __func__, ret);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
@@ -3612,13 +3595,13 @@ int glink_end_rx_rt(void *handle)
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EBUSY;
}
ret = ctx->transport_ptr->ops->rx_rt_unvote(ctx->transport_ptr->ops);
ctx->rt_vote_off++;
GLINK_INFO_CH(ctx, "%s: Unvoting RX Realtime Thread %d", __func__, ret);
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ret;
}
@@ -3704,10 +3687,10 @@ int glink_wait_link_down(void *handle)
if (ret)
return ret;
if (!ctx->transport_ptr) {
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return -EOPNOTSUPP;
}
- glink_put_ch_ctx(ctx, false);
+ glink_put_ch_ctx(ctx);
return ctx->transport_ptr->ops->wait_link_down(ctx->transport_ptr->ops);
}
EXPORT_SYMBOL(glink_wait_link_down);
@@ -4143,6 +4126,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 =
@@ -6248,7 +6232,6 @@ EXPORT_SYMBOL(glink_get_xprt_log_ctx);
static int glink_init(void)
{
log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "glink", 0);
- rwlock_init(&magic_lock);
if (!log_ctx)
GLINK_ERR("%s: unable to create log context\n", __func__);
glink_debugfs_init();
diff --git a/drivers/soc/qcom/glink_private.h b/drivers/soc/qcom/glink_private.h
index 24053c853a83..bb794f45cff7 100644
--- a/drivers/soc/qcom/glink_private.h
+++ b/drivers/soc/qcom/glink_private.h
@@ -745,7 +745,6 @@ struct subsys_info {
* ssr_name: Name of the subsystem recognized by the SSR framework
* edge: Name of the G-Link edge
* xprt: Name of the G-Link transport
- * restarted: Indicates whether a restart has been triggered for this edge
* cb_data: Private callback data structure for notification functions
* notify_list_node: used to chain this structure in the notify list
*/
@@ -753,7 +752,6 @@ struct subsys_info_leaf {
const char *ssr_name;
const char *edge;
const char *xprt;
- bool restarted;
struct ssr_notify_data *cb_data;
struct list_head notify_list_node;
};
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 168db46084df..37193bbb23b7 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -215,7 +215,6 @@ struct edge_info {
bool tx_blocked_signal_sent;
struct kthread_work kwork;
struct kthread_worker kworker;
- struct work_struct wakeup_work;
struct task_struct *task;
struct tasklet_struct tasklet;
struct srcu_struct use_ref;
@@ -816,6 +815,32 @@ static bool get_rx_fifo(struct edge_info *einfo)
}
/**
+ * tx_wakeup_worker() - worker function to wakeup tx blocked thread
+ * @work: kwork associated with the edge to process commands on.
+ */
+static void tx_wakeup_worker(struct edge_info *einfo)
+{
+ bool trigger_wakeup = false;
+ unsigned long flags;
+
+ if (einfo->in_ssr)
+ return;
+ if (einfo->tx_resume_needed && fifo_write_avail(einfo)) {
+ einfo->tx_resume_needed = false;
+ einfo->xprt_if.glink_core_if_ptr->tx_resume(
+ &einfo->xprt_if);
+ }
+ spin_lock_irqsave(&einfo->write_lock, flags);
+ if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/
+ einfo->tx_blocked_signal_sent = false;
+ trigger_wakeup = true;
+ }
+ spin_unlock_irqrestore(&einfo->write_lock, flags);
+ if (trigger_wakeup)
+ wake_up_all(&einfo->tx_blocked_queue);
+}
+
+/**
* __rx_worker() - process received commands on a specific edge
* @einfo: Edge to process commands on.
* @atomic_ctx: Indicates if the caller is in atomic context and requires any
@@ -849,7 +874,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;
@@ -865,7 +890,7 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx)
if ((atomic_ctx) && ((einfo->tx_resume_needed) ||
(waitqueue_active(&einfo->tx_blocked_queue)))) /* tx waiting ?*/
- schedule_work(&einfo->wakeup_work);
+ tx_wakeup_worker(einfo);
/*
* Access to the fifo needs to be synchronized, however only the calls
@@ -1173,39 +1198,6 @@ static void rx_worker_atomic(unsigned long param)
}
/**
- * tx_wakeup_worker() - worker function to wakeup tx blocked thread
- * @work: kwork associated with the edge to process commands on.
- */
-static void tx_wakeup_worker(struct work_struct *work)
-{
- struct edge_info *einfo;
- bool trigger_wakeup = false;
- unsigned long flags;
- int rcu_id;
-
- einfo = container_of(work, struct edge_info, wakeup_work);
- rcu_id = srcu_read_lock(&einfo->use_ref);
- if (einfo->in_ssr) {
- srcu_read_unlock(&einfo->use_ref, rcu_id);
- return;
- }
- if (einfo->tx_resume_needed && fifo_write_avail(einfo)) {
- einfo->tx_resume_needed = false;
- einfo->xprt_if.glink_core_if_ptr->tx_resume(
- &einfo->xprt_if);
- }
- spin_lock_irqsave(&einfo->write_lock, flags);
- if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/
- einfo->tx_blocked_signal_sent = false;
- trigger_wakeup = true;
- }
- spin_unlock_irqrestore(&einfo->write_lock, flags);
- if (trigger_wakeup)
- wake_up_all(&einfo->tx_blocked_queue);
- srcu_read_unlock(&einfo->use_ref, rcu_id);
-}
-
-/**
* rx_worker() - worker function to process received commands
* @work: kwork associated with the edge to process commands on.
*/
@@ -2355,7 +2347,6 @@ static int glink_smem_native_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
- INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->read_from_fifo = read_from_fifo;
einfo->write_to_fifo = write_to_fifo;
@@ -2411,7 +2402,7 @@ static int glink_smem_native_probe(struct platform_device *pdev)
einfo->tx_fifo = smem_alloc(SMEM_GLINK_NATIVE_XPRT_FIFO_0,
einfo->tx_fifo_size,
einfo->remote_proc_id,
- SMEM_ITEM_CACHED_FLAG);
+ 0);
if (!einfo->tx_fifo) {
pr_err("%s: smem alloc of tx fifo failed\n", __func__);
rc = -ENOMEM;
@@ -2457,7 +2448,6 @@ request_irq_fail:
reg_xprt_fail:
smem_alloc_fail:
flush_kthread_worker(&einfo->kworker);
- flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
@@ -2546,7 +2536,6 @@ static int glink_rpm_native_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
- INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->intentless = true;
einfo->read_from_fifo = memcpy32_fromio;
@@ -2707,7 +2696,6 @@ request_irq_fail:
reg_xprt_fail:
toc_init_fail:
flush_kthread_worker(&einfo->kworker);
- flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
@@ -2839,7 +2827,6 @@ static int glink_mailbox_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
- INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->read_from_fifo = read_from_fifo;
einfo->write_to_fifo = write_to_fifo;
@@ -2960,7 +2947,6 @@ request_irq_fail:
reg_xprt_fail:
smem_alloc_fail:
flush_kthread_worker(&einfo->kworker);
- flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c
index 7e23b0bc3852..c28eeab92fed 100644
--- a/drivers/soc/qcom/glink_ssr.c
+++ b/drivers/soc/qcom/glink_ssr.c
@@ -22,7 +22,6 @@
#include <linux/random.h>
#include <soc/qcom/glink.h>
#include <soc/qcom/subsystem_notif.h>
-#include <soc/qcom/subsystem_restart.h>
#include "glink_private.h"
#define GLINK_SSR_REPLY_TIMEOUT HZ
@@ -608,13 +607,9 @@ int notify_for_subsystem(struct subsys_info *ss_info)
kfree(do_cleanup_data);
ss_leaf_entry->cb_data->do_cleanup_data = NULL;
- if (strcmp(ss_leaf_entry->ssr_name, "rpm")) {
- subsystem_restart(ss_leaf_entry->ssr_name);
- ss_leaf_entry->restarted = true;
- } else {
+ if (!strcmp(ss_leaf_entry->ssr_name, "rpm"))
panic("%s: Could not queue intent for RPM!\n",
__func__);
- }
atomic_dec(&responses_remaining);
kref_put(&ss_leaf_entry->cb_data->cb_kref,
cb_data_release);
@@ -639,13 +634,9 @@ int notify_for_subsystem(struct subsys_info *ss_info)
kfree(do_cleanup_data);
ss_leaf_entry->cb_data->do_cleanup_data = NULL;
- if (strcmp(ss_leaf_entry->ssr_name, "rpm")) {
- subsystem_restart(ss_leaf_entry->ssr_name);
- ss_leaf_entry->restarted = true;
- } else {
+ if (!strcmp(ss_leaf_entry->ssr_name, "rpm"))
panic("%s: glink_tx() to RPM failed!\n",
__func__);
- }
atomic_dec(&responses_remaining);
kref_put(&ss_leaf_entry->cb_data->cb_kref,
cb_data_release);
@@ -687,11 +678,7 @@ int notify_for_subsystem(struct subsys_info *ss_info)
/* Check for RPM, as it can't be restarted */
if (!strcmp(ss_leaf_entry->ssr_name, "rpm"))
panic("%s: RPM failed to respond!\n", __func__);
- else if (!ss_leaf_entry->restarted)
- subsystem_restart(ss_leaf_entry->ssr_name);
}
- ss_leaf_entry->restarted = false;
-
if (!IS_ERR_OR_NULL(ss_leaf_entry->cb_data))
ss_leaf_entry->cb_data->responded = false;
kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release);
@@ -1011,7 +998,6 @@ static int glink_ssr_probe(struct platform_device *pdev)
ss_info_leaf->ssr_name = subsys_name;
ss_info_leaf->edge = edge;
ss_info_leaf->xprt = xprt;
- ss_info_leaf->restarted = false;
list_add_tail(&ss_info_leaf->notify_list_node,
&ss_info->notify_list);
ss_info->notify_list_len++;
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 43d954a0f7c7..21be894414bc 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,13 +261,14 @@ 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,
ICNSS_WDOG_BITE,
ICNSS_SHUTDOWN_DONE,
+ ICNSS_HOST_TRIGGERED_PDR,
};
struct ce_irq_list {
@@ -250,6 +321,13 @@ struct icnss_stats {
uint32_t disable;
} ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
+ struct {
+ uint32_t pdr_fw_crash;
+ uint32_t pdr_host_error;
+ uint32_t root_pd_crash;
+ uint32_t root_pd_shutdown;
+ } recovery;
+
uint32_t pm_suspend;
uint32_t pm_suspend_err;
uint32_t pm_resume;
@@ -290,7 +368,6 @@ struct icnss_stats {
uint32_t rejuvenate_ack_req;
uint32_t rejuvenate_ack_resp;
uint32_t rejuvenate_ack_err;
- uint32_t trigger_recovery;
};
#define MAX_NO_OF_MAC_ADDR 4
@@ -299,6 +376,20 @@ struct icnss_wlan_mac_addr {
uint32_t no_of_mac_addr_set;
};
+enum icnss_pdr_cause_index {
+ ICNSS_FW_CRASH,
+ ICNSS_ROOT_PD_CRASH,
+ ICNSS_ROOT_PD_SHUTDOWN,
+ ICNSS_HOST_ERROR,
+};
+
+static const char * const icnss_pdr_cause[] = {
+ [ICNSS_FW_CRASH] = "FW crash",
+ [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
+ [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
+ [ICNSS_HOST_ERROR] = "Host error",
+};
+
struct service_notifier_context {
void *handle;
uint32_t instance_id;
@@ -364,10 +455,9 @@ static struct icnss_priv {
bool is_wlan_mac_set;
struct icnss_wlan_mac_addr wlan_mac_addr;
bool bypass_s1_smmu;
+ struct mutex dev_lock;
} *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 +467,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 +1109,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 +1150,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 +1955,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 +1978,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,26 +2393,39 @@ 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",
priv->state, notif->crashed);
+ if (notif->crashed)
+ priv->stats.recovery.root_pd_crash++;
+ else
+ priv->stats.recovery.root_pd_shutdown++;
+
icnss_ignore_qmi_timeout(true);
event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
@@ -2402,14 +2461,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 +2482,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++)
@@ -2446,6 +2505,7 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
enum pd_subsys_state *state = data;
struct icnss_event_pd_service_down_data *event_data;
struct icnss_uevent_fw_down_data fw_down_data;
+ enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
notification, priv->state);
@@ -2460,26 +2520,40 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
if (state == NULL) {
event_data->crashed = true;
+ priv->stats.recovery.root_pd_crash++;
goto event_post;
}
- icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx\n",
- *state, priv->state);
-
switch (*state) {
case ROOT_PD_WDOG_BITE:
event_data->crashed = true;
event_data->wdog_bite = true;
+ priv->stats.recovery.root_pd_crash++;
break;
case ROOT_PD_SHUTDOWN:
+ cause = ICNSS_ROOT_PD_SHUTDOWN;
+ priv->stats.recovery.root_pd_shutdown++;
+ break;
+ case USER_PD_STATE_CHANGE:
+ if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
+ cause = ICNSS_HOST_ERROR;
+ priv->stats.recovery.pdr_host_error++;
+ } else {
+ cause = ICNSS_FW_CRASH;
+ priv->stats.recovery.pdr_fw_crash++;
+ }
break;
default:
event_data->crashed = true;
+ priv->stats.recovery.root_pd_crash++;
break;
}
+ icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
+ *state, priv->state, icnss_pdr_cause[cause]);
event_post:
icnss_ignore_qmi_timeout(true);
+ clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
fw_down_data.crashed = event_data->crashed;
icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
@@ -2547,9 +2621,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 +3279,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;
@@ -3221,7 +3296,6 @@ int icnss_trigger_recovery(struct device *dev)
WARN_ON(1);
icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
priv->state);
- priv->stats.trigger_recovery++;
/*
* Initiate PDR, required only for the first instance
@@ -3229,6 +3303,9 @@ int icnss_trigger_recovery(struct device *dev)
ret = service_notif_pd_restart(priv->service_notifier[0].name,
priv->service_notifier[0].instance_id);
+ if (!ret)
+ set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
+
out:
return ret;
}
@@ -3583,9 +3660,6 @@ static ssize_t icnss_fw_debug_write(struct file *fp,
if (ret)
return ret;
- if (ret == 0)
- memset(&priv->stats, 0, sizeof(priv->stats));
-
return count;
}
@@ -3658,11 +3732,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");
@@ -3679,6 +3753,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
case ICNSS_SHUTDOWN_DONE:
seq_puts(s, "SHUTDOWN DONE");
continue;
+ case ICNSS_HOST_TRIGGERED_PDR:
+ seq_puts(s, "HOST TRIGGERED PDR");
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
@@ -3777,7 +3854,10 @@ static int icnss_stats_show(struct seq_file *s, void *data)
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
- ICNSS_STATS_DUMP(s, priv, trigger_recovery);
+ ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
+ ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
+ ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
+ ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
seq_puts(s, "\n<------------------ PM stats ------------------->\n");
ICNSS_STATS_DUMP(s, priv, pm_suspend);
@@ -3906,12 +3986,14 @@ static int icnss_regread_show(struct seq_file *s, void *data)
{
struct icnss_priv *priv = s->private;
+ mutex_lock(&priv->dev_lock);
if (!priv->diag_reg_read_buf) {
seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
if (!test_bit(ICNSS_FW_READY, &priv->state))
seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
+ mutex_unlock(&priv->dev_lock);
return 0;
}
@@ -3925,6 +4007,7 @@ static int icnss_regread_show(struct seq_file *s, void *data)
priv->diag_reg_read_len = 0;
kfree(priv->diag_reg_read_buf);
priv->diag_reg_read_buf = NULL;
+ mutex_unlock(&priv->dev_lock);
return 0;
}
@@ -3985,18 +4068,22 @@ static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
return -EINVAL;
+ mutex_lock(&priv->dev_lock);
kfree(priv->diag_reg_read_buf);
priv->diag_reg_read_buf = NULL;
reg_buf = kzalloc(data_len, GFP_KERNEL);
- if (!reg_buf)
+ if (!reg_buf) {
+ mutex_unlock(&priv->dev_lock);
return -ENOMEM;
+ }
ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
mem_type, data_len,
reg_buf);
if (ret) {
kfree(reg_buf);
+ mutex_unlock(&priv->dev_lock);
return ret;
}
@@ -4004,6 +4091,7 @@ static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
priv->diag_reg_read_mem_type = mem_type;
priv->diag_reg_read_len = data_len;
priv->diag_reg_read_buf = reg_buf;
+ mutex_unlock(&priv->dev_lock);
return count;
}
@@ -4251,6 +4339,7 @@ static int icnss_probe(struct platform_device *pdev)
spin_lock_init(&priv->event_lock);
spin_lock_init(&priv->on_off_lock);
+ mutex_init(&priv->dev_lock);
priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
if (!priv->event_wq) {
@@ -4276,6 +4365,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 +4390,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 +4409,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/ipc_router_glink_xprt.c b/drivers/soc/qcom/ipc_router_glink_xprt.c
index 1f36dd0ba07e..7dd1683881fb 100644
--- a/drivers/soc/qcom/ipc_router_glink_xprt.c
+++ b/drivers/soc/qcom/ipc_router_glink_xprt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -70,6 +70,7 @@ if (ipc_router_glink_xprt_debug_mask) \
* @xprt_version: IPC Router header version supported by this XPRT.
* @xprt_option: XPRT specific options to be handled by IPC Router.
* @disable_pil_loading: Disable PIL Loading of the subsystem.
+ * @dynamic_wakeup_source: Dynamic wakeup source for this subsystem.
*/
struct ipc_router_glink_xprt {
struct list_head list;
@@ -91,6 +92,7 @@ struct ipc_router_glink_xprt {
uint32_t cur_lo_intents_cnt;
uint32_t cur_md_intents_cnt;
uint32_t cur_hi_intents_cnt;
+ bool dynamic_wakeup_source;
};
struct ipc_router_glink_xprt_work {
@@ -127,6 +129,7 @@ static void glink_xprt_close_event(struct work_struct *work);
* @link_id: Network Cluster ID to which this XPRT belongs to.
* @xprt_version: IPC Router header version supported by this XPRT.
* @disable_pil_loading:Disable PIL Loading of the subsystem.
+ * @dynamic_wakeup_source: Dynamic wakeup source for this subsystem.
*/
struct ipc_router_glink_xprt_config {
char ch_name[GLINK_NAME_SIZE];
@@ -138,6 +141,7 @@ struct ipc_router_glink_xprt_config {
unsigned xprt_version;
unsigned xprt_option;
bool disable_pil_loading;
+ bool dynamic_wakeup_source;
};
#define MODULE_NAME "ipc_router_glink_xprt"
@@ -292,6 +296,14 @@ static void glink_xprt_sft_close_done(struct msm_ipc_router_xprt *xprt)
complete_all(&glink_xprtp->sft_close_complete);
}
+static bool ipc_router_glink_xprt_get_ws_info(struct msm_ipc_router_xprt *xprt)
+{
+ struct ipc_router_glink_xprt *glink_xprtp =
+ container_of(xprt, struct ipc_router_glink_xprt, xprt);
+
+ return glink_xprtp->dynamic_wakeup_source;
+}
+
static struct rr_packet *glink_xprt_copy_data(struct read_work *rx_work)
{
void *buf, *pbuf, *dest_buf;
@@ -705,6 +717,8 @@ static int ipc_router_glink_config_init(
glink_xprtp->xprt_option = glink_xprt_config->xprt_option;
glink_xprtp->disable_pil_loading =
glink_xprt_config->disable_pil_loading;
+ glink_xprtp->dynamic_wakeup_source =
+ glink_xprt_config->dynamic_wakeup_source;
if (!glink_xprtp->disable_pil_loading)
strlcpy(glink_xprtp->pil_edge, glink_xprt_config->pil_edge,
@@ -727,6 +741,7 @@ static int ipc_router_glink_config_init(
glink_xprtp->xprt.write = ipc_router_glink_xprt_write;
glink_xprtp->xprt.close = ipc_router_glink_xprt_close;
glink_xprtp->xprt.sft_close_done = glink_xprt_sft_close_done;
+ glink_xprtp->xprt.get_ws_info = ipc_router_glink_xprt_get_ws_info;
glink_xprtp->xprt.priv = NULL;
init_rwsem(&glink_xprtp->ss_reset_rwlock);
@@ -821,6 +836,10 @@ static int parse_devicetree(struct device_node *node,
scnprintf(glink_xprt_config->ipc_rtr_xprt_name, IPC_RTR_XPRT_NAME_LEN,
"%s_%s", edge, ch_name);
+ key = "qcom,dynamic-wakeup-source";
+ glink_xprt_config->dynamic_wakeup_source =
+ of_property_read_bool(node, key);
+
return 0;
error:
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/soc/qcom/msm_bus/msm_bus_rules.c b/drivers/soc/qcom/msm_bus/msm_bus_rules.c
index ea29e303bbde..43a892bbd27c 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_rules.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_rules.c
@@ -597,7 +597,7 @@ void msm_rule_register(int num_rules, struct bus_rule_type *rule,
static bool __rule_unregister(int num_rules, struct bus_rule_type *rule,
struct notifier_block *nb)
{
- int i;
+ int i = 0;
struct rule_node_info *node = NULL;
struct rule_node_info *node_tmp = NULL;
struct rules_def *node_rule;
diff --git a/drivers/soc/qcom/msm_glink_pkt.c b/drivers/soc/qcom/msm_glink_pkt.c
index a92e5c416678..0a80016f1942 100644
--- a/drivers/soc/qcom/msm_glink_pkt.c
+++ b/drivers/soc/qcom/msm_glink_pkt.c
@@ -33,7 +33,8 @@
#include <linux/termios.h>
#include <soc/qcom/glink.h>
-
+/* This Limit ensures that auto queue will not exhaust memory on remote side */
+#define MAX_PENDING_GLINK_PKT 5
#define MODULE_NAME "msm_glinkpkt"
#define DEVICE_NAME "glinkpkt"
#define WAKEUPSOURCE_TIMEOUT (2000) /* two seconds */
@@ -136,6 +137,7 @@ struct glink_pkt_dev {
struct glink_link_info link_info;
void *link_state_handle;
bool link_up;
+ bool auto_intent_enabled;
};
/**
@@ -447,8 +449,26 @@ void glink_pkt_notify_state(void *handle, const void *priv, unsigned event)
bool glink_pkt_rmt_rx_intent_req_cb(void *handle, const void *priv, size_t sz)
{
struct queue_rx_intent_work *work_item;
+ int pending_pkt_count = 0;
+ struct glink_rx_pkt *pkt = NULL;
+ unsigned long flags;
+ struct glink_pkt_dev *devp = (struct glink_pkt_dev *)priv;
+
GLINK_PKT_INFO("%s(): QUEUE RX INTENT to receive size[%zu]\n",
__func__, sz);
+ if (devp->auto_intent_enabled) {
+ spin_lock_irqsave(&devp->pkt_list_lock, flags);
+ list_for_each_entry(pkt, &devp->pkt_list, list)
+ pending_pkt_count++;
+ spin_unlock_irqrestore(&devp->pkt_list_lock, flags);
+ if (pending_pkt_count > MAX_PENDING_GLINK_PKT) {
+ GLINK_PKT_ERR("%s failed, max limit reached\n",
+ __func__);
+ return false;
+ }
+ } else {
+ return false;
+ }
work_item = kzalloc(sizeof(*work_item), GFP_ATOMIC);
if (!work_item) {
@@ -457,7 +477,7 @@ bool glink_pkt_rmt_rx_intent_req_cb(void *handle, const void *priv, size_t sz)
}
work_item->intent_size = sz;
- work_item->devp = (struct glink_pkt_dev *)priv;
+ work_item->devp = devp;
INIT_WORK(&work_item->work, glink_pkt_queue_rx_intent_worker);
queue_work(glink_pkt_wq, &work_item->work);
@@ -626,10 +646,11 @@ ssize_t glink_pkt_read(struct file *file,
}
mutex_lock(&devp->ch_lock);
- if (!glink_rx_intent_exists(devp->handle, count)) {
+ if (!glink_pkt_read_avail(devp) &&
+ !glink_rx_intent_exists(devp->handle, count)) {
ret = glink_queue_rx_intent(devp->handle, devp, count);
if (ret) {
- GLINK_PKT_ERR("%s: failed to queue_rx_intent ret[%d]\n",
+ GLINK_PKT_ERR("%s: failed to queue intent ret[%d]\n",
__func__, ret);
mutex_unlock(&devp->ch_lock);
return ret;
@@ -915,6 +936,7 @@ static long glink_pkt_ioctl(struct file *file, unsigned int cmd,
case GLINK_PKT_IOCTL_QUEUE_RX_INTENT:
ret = get_user(size, (uint32_t *)arg);
GLINK_PKT_INFO("%s: intent size[%d]\n", __func__, size);
+ devp->auto_intent_enabled = false;
ret = glink_queue_rx_intent(devp->handle, devp, size);
if (ret) {
GLINK_PKT_ERR("%s: failed to QUEUE_RX_INTENT ret[%d]\n",
@@ -1183,6 +1205,7 @@ static int glink_pkt_init_add_device(struct glink_pkt_dev *devp, int i)
glink_pkt_link_state_cb;
devp->i = i;
devp->poll_mode = 0;
+ devp->auto_intent_enabled = true;
devp->ws_locked = 0;
devp->ch_state = GLINK_LOCAL_DISCONNECTED;
/* Default timeout for open wait is 120sec */
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index ed8006cacc08..ace2bc4f30b6 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -39,6 +39,8 @@
#include <asm/uaccess.h>
#include <asm/setup.h>
#include <asm-generic/io-64-nonatomic-lo-hi.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/trace_msm_pil_event.h>
#include "peripheral-loader.h"
@@ -835,6 +837,7 @@ int pil_boot(struct pil_desc *desc)
goto release_fw;
}
+ trace_pil_event("before_init_image", desc);
if (desc->ops->init_image)
ret = desc->ops->init_image(desc, fw->data, fw->size);
if (ret) {
@@ -843,6 +846,7 @@ int pil_boot(struct pil_desc *desc)
goto err_boot;
}
+ trace_pil_event("before_mem_setup", desc);
if (desc->ops->mem_setup)
ret = desc->ops->mem_setup(desc, priv->region_start,
priv->region_end - priv->region_start);
@@ -858,6 +862,7 @@ int pil_boot(struct pil_desc *desc)
* Also for secure boot devices, modem memory has to be released
* after MBA is booted
*/
+ trace_pil_event("before_assign_mem", desc);
if (desc->modem_ssr) {
ret = pil_assign_mem_to_linux(desc, priv->region_start,
(priv->region_end - priv->region_start));
@@ -876,6 +881,7 @@ int pil_boot(struct pil_desc *desc)
hyp_assign = true;
}
+ trace_pil_event("before_load_seg", desc);
list_for_each_entry(seg, &desc->priv->segs, list) {
ret = pil_load_seg(desc, seg);
if (ret)
@@ -883,6 +889,7 @@ int pil_boot(struct pil_desc *desc)
}
if (desc->subsys_vmid > 0) {
+ trace_pil_event("before_reclaim_mem", desc);
ret = pil_reclaim_mem(desc, priv->region_start,
(priv->region_end - priv->region_start),
desc->subsys_vmid);
@@ -894,12 +901,14 @@ int pil_boot(struct pil_desc *desc)
hyp_assign = false;
}
+ trace_pil_event("before_auth_reset", desc);
ret = desc->ops->auth_and_reset(desc);
if (ret) {
pil_err(desc, "Failed to bring out of reset(rc:%d)\n", ret);
subsys_set_error(desc->subsys_dev, firmware_error_msg);
goto err_auth_and_reset;
}
+ trace_pil_event("reset_done", desc);
pil_info(desc, "Brought out of reset\n");
desc->modem_ssr = false;
err_auth_and_reset:
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 988b6e8c9fd9..5fcb0f95733c 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -26,6 +26,7 @@
#include <linux/highmem.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/secure_buffer.h>
+#include <trace/events/trace_msm_pil_event.h>
#include "peripheral-loader.h"
#include "pil-q6v5.h"
@@ -462,6 +463,7 @@ static int pil_mss_reset(struct pil_desc *pil)
phys_addr_t start_addr = pil_get_entry_addr(pil);
int ret;
+ trace_pil_func(__func__);
if (drv->mba_dp_phys)
start_addr = drv->mba_dp_phys;
@@ -555,6 +557,7 @@ int pil_mss_reset_load_mba(struct pil_desc *pil)
const u8 *data;
struct device *dma_dev = md->mba_mem_dev_fixed ?: &md->mba_mem_dev;
+ trace_pil_func(__func__);
fw_name_p = drv->non_elf_image ? fw_name_legacy : fw_name;
ret = request_firmware(&fw, fw_name_p, pil->dev);
if (ret) {
@@ -685,6 +688,7 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata,
DEFINE_DMA_ATTRS(attrs);
+ trace_pil_func(__func__);
dma_dev->coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
dma_set_attr(DMA_ATTR_SKIP_ZEROING, &attrs);
dma_set_attr(DMA_ATTR_STRONGLY_ORDERED, &attrs);
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index 34228f072b28..15415e78f98e 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -22,6 +22,7 @@
#include <linux/regulator/consumer.h>
#include <linux/regulator/rpm-smd-regulator.h>
#include <linux/clk/msm-clk.h>
+#include <trace/events/trace_msm_pil_event.h>
#include "peripheral-loader.h"
#include "pil-q6v5.h"
@@ -360,6 +361,7 @@ static int __pil_q6v55_reset(struct pil_desc *pil)
u32 val;
int i;
+ trace_pil_func(__func__);
/* Override the ACC value if required */
if (drv->override_acc)
writel_relaxed(QDSP6SS_ACC_OVERRIDE_VAL,
diff --git a/drivers/soc/qcom/rpm-smd-debug.c b/drivers/soc/qcom/rpm-smd-debug.c
index 4e406f7cd379..6ef90b23aed5 100644
--- a/drivers/soc/qcom/rpm-smd-debug.c
+++ b/drivers/soc/qcom/rpm-smd-debug.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-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
@@ -44,9 +44,9 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer,
{
char buf[MAX_MSG_BUFFER], rsc_type_str[6] = {}, rpm_set[8] = {},
key_str[6] = {};
- int i, pos, set = -1, nelems;
+ int i, pos = -1, set = -1, nelems = -1;
char *cmp;
- uint32_t rsc_type, rsc_id, key, data;
+ uint32_t rsc_type = 0, rsc_id = 0, key = 0, data = 0;
struct msm_rpm_request *req;
count = min(count, sizeof(buf) - 1);
@@ -55,8 +55,12 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer,
buf[count] = '\0';
cmp = strstrip(buf);
- sscanf(cmp, "%7s %5s %u %d %n", rpm_set, rsc_type_str, &rsc_id,
- &nelems, &pos);
+ if (sscanf(cmp, "%7s %5s %u %d %n", rpm_set, rsc_type_str,
+ &rsc_id, &nelems, &pos) != 4) {
+ pr_err("Invalid number of arguments passed\n");
+ goto err;
+ }
+
if (strlen(rpm_set) > 6 || strlen(rsc_type_str) > 4) {
pr_err("Invalid value of set or resource type\n");
goto err;
@@ -84,7 +88,11 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer,
for (i = 0; i < nelems; i++) {
cmp += pos;
- sscanf(cmp, "%5s %n", key_str, &pos);
+ if (sscanf(cmp, "%5s %n", key_str, &pos) != 1) {
+ pr_err("Invalid number of arguments passed\n");
+ goto err;
+ }
+
if (strlen(key_str) > 4) {
pr_err("Key value cannot be more than 4 charecters");
goto err;
@@ -96,7 +104,11 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer,
}
cmp += pos;
- sscanf(cmp, "%u %n", &data, &pos);
+ if (sscanf(cmp, "%u %n", &data, &pos) != 1) {
+ pr_err("Invalid number of arguments passed\n");
+ goto err;
+ }
+
if (msm_rpm_add_kvp_data(req, key,
(void *)&data, sizeof(data)))
goto err_request;
diff --git a/drivers/soc/qcom/rpm_master_stat.c b/drivers/soc/qcom/rpm_master_stat.c
index 7bf18ffe6ad2..b8bf3a059677 100644
--- a/drivers/soc/qcom/rpm_master_stat.c
+++ b/drivers/soc/qcom/rpm_master_stat.c
@@ -50,6 +50,8 @@
#define GET_FIELD(a) ((strnstr(#a, ".", 80) + 1))
+static DEFINE_MUTEX(msm_rpm_master_stats_mutex);
+
struct msm_rpm_master_stats {
uint32_t active_cores;
uint32_t numshutdowns;
@@ -80,9 +82,11 @@ int msm_rpm_master_stats_file_close(struct inode *inode,
{
struct msm_rpm_master_stats_private_data *private = file->private_data;
+ mutex_lock(&msm_rpm_master_stats_mutex);
if (private->reg_base)
iounmap(private->reg_base);
kfree(file->private_data);
+ mutex_unlock(&msm_rpm_master_stats_mutex);
return 0;
}
@@ -95,15 +99,11 @@ static int msm_rpm_master_copy_stats(
static int master_cnt;
int count, j = 0;
char *buf;
- static DEFINE_MUTEX(msm_rpm_master_stats_mutex);
unsigned long active_cores;
- mutex_lock(&msm_rpm_master_stats_mutex);
-
/* Iterate possible number of masters */
if (master_cnt > prvdata->num_masters - 1) {
master_cnt = 0;
- mutex_unlock(&msm_rpm_master_stats_mutex);
return 0;
}
@@ -256,7 +256,6 @@ static int msm_rpm_master_copy_stats(
}
master_cnt++;
- mutex_unlock(&msm_rpm_master_stats_mutex);
return RPM_MASTERS_BUF_LEN - count;
}
@@ -265,25 +264,36 @@ static ssize_t msm_rpm_master_stats_file_read(struct file *file,
{
struct msm_rpm_master_stats_private_data *prvdata;
struct msm_rpm_master_stats_platform_data *pdata;
+ ssize_t ret;
+ mutex_lock(&msm_rpm_master_stats_mutex);
prvdata = file->private_data;
- if (!prvdata)
- return -EINVAL;
+ if (!prvdata) {
+ ret = -EINVAL;
+ goto exit;
+ }
pdata = prvdata->platform_data;
- if (!pdata)
- return -EINVAL;
+ if (!pdata) {
+ ret = -EINVAL;
+ goto exit;
+ }
- if (!bufu || count == 0)
- return -EINVAL;
+ if (!bufu || count == 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
if (*ppos <= pdata->phys_size) {
prvdata->len = msm_rpm_master_copy_stats(prvdata);
*ppos = 0;
}
- return simple_read_from_buffer(bufu, count, ppos,
+ ret = simple_read_from_buffer(bufu, count, ppos,
prvdata->buf, prvdata->len);
+exit:
+ mutex_unlock(&msm_rpm_master_stats_mutex);
+ return ret;
}
static int msm_rpm_master_stats_file_open(struct inode *inode,
@@ -291,15 +301,20 @@ static int msm_rpm_master_stats_file_open(struct inode *inode,
{
struct msm_rpm_master_stats_private_data *prvdata;
struct msm_rpm_master_stats_platform_data *pdata;
+ int ret = 0;
+ mutex_lock(&msm_rpm_master_stats_mutex);
pdata = inode->i_private;
file->private_data =
kzalloc(sizeof(struct msm_rpm_master_stats_private_data),
GFP_KERNEL);
- if (!file->private_data)
- return -ENOMEM;
+ if (!file->private_data) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
prvdata = file->private_data;
prvdata->reg_base = ioremap(pdata->phys_addr_base,
@@ -310,14 +325,17 @@ static int msm_rpm_master_stats_file_open(struct inode *inode,
pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
__func__, &pdata->phys_addr_base,
pdata->phys_size);
- return -EBUSY;
+ ret = -EBUSY;
+ goto exit;
}
prvdata->len = 0;
prvdata->num_masters = pdata->num_masters;
prvdata->master_names = pdata->masters;
prvdata->platform_data = pdata;
- return 0;
+exit:
+ mutex_unlock(&msm_rpm_master_stats_mutex);
+ return ret;
}
static const struct file_operations msm_rpm_master_stats_fops = {
diff --git a/drivers/soc/qcom/rpm_rail_stats.c b/drivers/soc/qcom/rpm_rail_stats.c
index 9ef96dc54eb6..728fb69bfbe2 100644
--- a/drivers/soc/qcom/rpm_rail_stats.c
+++ b/drivers/soc/qcom/rpm_rail_stats.c
@@ -46,6 +46,8 @@
#define NAMELEN (sizeof(uint32_t)+1)
+static DEFINE_MUTEX(msm_rpm_rail_stats_mutex);
+
struct msm_rpm_rail_stats_platform_data {
phys_addr_t phys_addr_base;
u32 phys_size;
@@ -80,9 +82,11 @@ int msm_rpm_rail_stats_file_close(struct inode *inode, struct file *file)
{
struct msm_rpm_rail_stats_private_data *private = file->private_data;
+ mutex_lock(&msm_rpm_rail_stats_mutex);
if (private->reg_base)
iounmap(private->reg_base);
kfree(file->private_data);
+ mutex_unlock(&msm_rpm_rail_stats_mutex);
return 0;
}
@@ -154,18 +158,26 @@ static int msm_rpm_rail_stats_copy(
static ssize_t msm_rpm_rail_stats_file_read(struct file *file,
char __user *bufu, size_t count, loff_t *ppos)
{
- struct msm_rpm_rail_stats_private_data *prvdata =
- file->private_data;
+ struct msm_rpm_rail_stats_private_data *prvdata;
struct msm_rpm_rail_stats_platform_data *pdata;
+ ssize_t ret;
- if (!prvdata)
- return -EINVAL;
+ mutex_lock(&msm_rpm_rail_stats_mutex);
+ prvdata = file->private_data;
+ if (!prvdata) {
+ ret = -EINVAL;
+ goto exit;
+ }
- if (!prvdata->platform_data)
- return -EINVAL;
+ if (!prvdata->platform_data) {
+ ret = -EINVAL;
+ goto exit;
+ }
- if (!bufu || count == 0)
- return -EINVAL;
+ if (!bufu || count == 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
pdata = prvdata->platform_data;
@@ -174,22 +186,32 @@ static ssize_t msm_rpm_rail_stats_file_read(struct file *file,
*ppos = 0;
}
- return simple_read_from_buffer(bufu, count, ppos,
+ ret = simple_read_from_buffer(bufu, count, ppos,
prvdata->buf, prvdata->len);
+exit:
+ mutex_unlock(&msm_rpm_rail_stats_mutex);
+ return ret;
}
static int msm_rpm_rail_stats_file_open(struct inode *inode,
struct file *file)
{
struct msm_rpm_rail_stats_private_data *prvdata;
- struct msm_rpm_rail_stats_platform_data *pdata = inode->i_private;
+ struct msm_rpm_rail_stats_platform_data *pdata;
+ int ret = 0;
+
+ mutex_lock(&msm_rpm_rail_stats_mutex);
+ pdata = inode->i_private;
file->private_data =
kzalloc(sizeof(struct msm_rpm_rail_stats_private_data),
GFP_KERNEL);
- if (!file->private_data)
- return -ENOMEM;
+ if (!file->private_data) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
prvdata = file->private_data;
prvdata->reg_base = ioremap(pdata->phys_addr_base,
@@ -200,12 +222,15 @@ static int msm_rpm_rail_stats_file_open(struct inode *inode,
pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
__func__, &pdata->phys_addr_base,
pdata->phys_size);
- return -EBUSY;
+ ret = -EBUSY;
+ goto exit;
}
prvdata->len = 0;
prvdata->platform_data = pdata;
- return 0;
+exit:
+ mutex_unlock(&msm_rpm_rail_stats_mutex);
+ return ret;
}
diff --git a/drivers/soc/qcom/rpm_stats.c b/drivers/soc/qcom/rpm_stats.c
index 8f3094853ba3..b54af9eae8ec 100644
--- a/drivers/soc/qcom/rpm_stats.c
+++ b/drivers/soc/qcom/rpm_stats.c
@@ -31,6 +31,8 @@
#define GET_PDATA_OF_ATTR(attr) \
(container_of(attr, struct msm_rpmstats_kobj_attr, ka)->pd)
+static DEFINE_MUTEX(rpm_stats_mutex);
+
enum {
ID_COUNTER,
ID_ACCUM_TIME_SCLK,
@@ -220,6 +222,12 @@ static int msm_rpmstats_copy_stats(struct msm_rpmstats_private_data *pdata)
record.id = msm_rpmstats_read_register(pdata->reg_base,
pdata->read_idx, 1);
+ if (record.id >= ID_MAX) {
+ pr_err("%s: array out of bound error found.\n",
+ __func__);
+ return -EINVAL;
+ }
+
record.val = msm_rpmstats_read_register(pdata->reg_base,
pdata->read_idx, 2);
@@ -242,13 +250,20 @@ static ssize_t msm_rpmstats_file_read(struct file *file, char __user *bufu,
size_t count, loff_t *ppos)
{
struct msm_rpmstats_private_data *prvdata;
+ ssize_t ret;
+ mutex_lock(&rpm_stats_mutex);
prvdata = file->private_data;
- if (!prvdata)
- return -EINVAL;
- if (!bufu || count == 0)
- return -EINVAL;
+ if (!prvdata) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (!bufu || count == 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
if (prvdata->platform_data->version == 1) {
if (!prvdata->num_records)
@@ -263,22 +278,30 @@ static ssize_t msm_rpmstats_file_read(struct file *file, char __user *bufu,
prvdata->len = msm_rpmstats_copy_stats_v2(prvdata);
*ppos = 0;
}
- return simple_read_from_buffer(bufu, count, ppos,
+ ret = simple_read_from_buffer(bufu, count, ppos,
prvdata->buf, prvdata->len);
+exit:
+ mutex_unlock(&rpm_stats_mutex);
+ return ret;
}
static int msm_rpmstats_file_open(struct inode *inode, struct file *file)
{
struct msm_rpmstats_private_data *prvdata;
struct msm_rpmstats_platform_data *pdata;
+ int ret = 0;
+ mutex_lock(&rpm_stats_mutex);
pdata = inode->i_private;
file->private_data =
kmalloc(sizeof(struct msm_rpmstats_private_data), GFP_KERNEL);
- if (!file->private_data)
- return -ENOMEM;
+ if (!file->private_data) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
prvdata = file->private_data;
prvdata->reg_base = ioremap_nocache(pdata->phys_addr_base,
@@ -289,24 +312,28 @@ static int msm_rpmstats_file_open(struct inode *inode, struct file *file)
pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
__func__, &pdata->phys_addr_base,
pdata->phys_size);
- return -EBUSY;
+ ret = -EBUSY;
+ goto exit;
}
prvdata->read_idx = prvdata->num_records = prvdata->len = 0;
prvdata->platform_data = pdata;
if (pdata->version == 2)
prvdata->num_records = 2;
-
- return 0;
+exit:
+ mutex_unlock(&rpm_stats_mutex);
+ return ret;
}
static int msm_rpmstats_file_close(struct inode *inode, struct file *file)
{
struct msm_rpmstats_private_data *private = file->private_data;
+ mutex_lock(&rpm_stats_mutex);
if (private->reg_base)
iounmap(private->reg_base);
kfree(file->private_data);
+ mutex_unlock(&rpm_stats_mutex);
return 0;
}
@@ -362,22 +389,26 @@ static ssize_t rpmstats_show(struct kobject *kobj,
{
struct msm_rpmstats_private_data *prvdata = NULL;
struct msm_rpmstats_platform_data *pdata = NULL;
+ ssize_t ret;
+ mutex_lock(&rpm_stats_mutex);
pdata = GET_PDATA_OF_ATTR(attr);
prvdata =
kmalloc(sizeof(*prvdata), GFP_KERNEL);
- if (!prvdata)
- return -ENOMEM;
+ if (!prvdata) {
+ ret = -ENOMEM;
+ goto kmalloc_fail;
+ }
prvdata->reg_base = ioremap_nocache(pdata->phys_addr_base,
pdata->phys_size);
if (!prvdata->reg_base) {
- kfree(prvdata);
pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
__func__, &pdata->phys_addr_base,
pdata->phys_size);
- return -EBUSY;
+ ret = -EBUSY;
+ goto ioremap_fail;
}
prvdata->read_idx = prvdata->num_records = prvdata->len = 0;
@@ -399,23 +430,22 @@ static ssize_t rpmstats_show(struct kobject *kobj,
prvdata);
}
- return snprintf(buf, prvdata->len, prvdata->buf);
+ ret = snprintf(buf, prvdata->len, prvdata->buf);
+ iounmap(prvdata->reg_base);
+ioremap_fail:
+ kfree(prvdata);
+kmalloc_fail:
+ mutex_unlock(&rpm_stats_mutex);
+ return ret;
}
static int msm_rpmstats_create_sysfs(struct msm_rpmstats_platform_data *pd)
{
- struct kobject *module_kobj = NULL;
struct kobject *rpmstats_kobj = NULL;
struct msm_rpmstats_kobj_attr *rpms_ka = NULL;
int ret = 0;
- module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
- if (!module_kobj) {
- pr_err("%s: Cannot find module_kset\n", __func__);
- return -ENODEV;
- }
-
- rpmstats_kobj = kobject_create_and_add("rpmstats", module_kobj);
+ rpmstats_kobj = kobject_create_and_add("system_sleep", power_kobj);
if (!rpmstats_kobj) {
pr_err("%s: Cannot create rpmstats kobject\n", __func__);
ret = -ENOMEM;
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index 36804b988d62..31de6e5c173c 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -731,10 +731,6 @@ int scm_call2_atomic(u32 fn_id, struct scm_desc *desc)
x0 = fn_id | BIT(SMC_ATOMIC_SYSCALL) | scm_version_mask;
- pr_debug("scm_call: func id %#llx, args: %#x, %#llx, %#llx, %#llx, %#llx\n",
- x0, desc->arginfo, desc->args[0], desc->args[1],
- desc->args[2], desc->x5);
-
if (scm_version == SCM_ARMV8_64)
ret = __scm_call_armv8_64(x0, desc->arginfo, desc->args[0],
desc->args[1], desc->args[2],
@@ -746,9 +742,8 @@ int scm_call2_atomic(u32 fn_id, struct scm_desc *desc)
desc->x5, &desc->ret[0],
&desc->ret[1], &desc->ret[2]);
if (ret < 0)
- pr_err("scm_call failed: func id %#llx, arginfo: %#x, args: %#llx, %#llx, %#llx, %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n",
- x0, desc->arginfo, desc->args[0], desc->args[1],
- desc->args[2], desc->x5, ret, desc->ret[0],
+ pr_err("scm_call failed: func id %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n",
+ x0, ret, desc->ret[0],
desc->ret[1], desc->ret[2]);
if (arglen > N_REGISTER_ARGS)
diff --git a/drivers/soc/qcom/smp2p_sleepstate.c b/drivers/soc/qcom/smp2p_sleepstate.c
index 34b46d25a823..2ef25e48ce50 100644
--- a/drivers/soc/qcom/smp2p_sleepstate.c
+++ b/drivers/soc/qcom/smp2p_sleepstate.c
@@ -14,6 +14,8 @@
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
+#include <linux/delay.h>
+#include <linux/ipc_router.h>
#include "smp2p_private.h"
#define SET_DELAY (2 * HZ)
@@ -35,10 +37,14 @@ static int sleepstate_pm_notifier(struct notifier_block *nb,
switch (event) {
case PM_SUSPEND_PREPARE:
gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 0);
+ msleep(25); /* To be tuned based on SMP2P latencies */
+ msm_ipc_router_set_ws_allowed(true);
break;
case PM_POST_SUSPEND:
gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 1);
+ msleep(25); /* To be tuned based on SMP2P latencies */
+ msm_ipc_router_set_ws_allowed(false);
break;
}
return NOTIFY_DONE;
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index d1f300e6afb7..7f03aabf415e 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -96,8 +96,6 @@
/* SPCOM driver name */
#define DEVICE_NAME "spcom"
-#define SPCOM_MAX_CHANNELS 0x20
-
/* maximum ION buffers should be >= SPCOM_MAX_CHANNELS */
#define SPCOM_MAX_ION_BUF_PER_CH (SPCOM_MAX_CHANNELS + 4)
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index 991bce363740..f85c4ba06b47 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -1113,6 +1113,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev)
rc = PTR_ERR(d->subsys);
goto err_subsys;
}
+ d->desc.subsys_dev = d->subsys;
return 0;
err_subsys:
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index 6afe2fb8cd75..ae249f382339 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -35,6 +35,7 @@
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/sysmon.h>
+#include <trace/events/trace_msm_pil_event.h>
#include <asm/current.h>
@@ -539,8 +540,10 @@ static void notify_each_subsys_device(struct subsys_device **list,
notif_data.no_auth = dev->desc->no_auth;
notif_data.pdev = pdev;
+ trace_pil_notif("before_send_notif", notif, dev->desc->fw_name);
subsys_notif_queue_notification(dev->notify, notif,
&notif_data);
+ trace_pil_notif("after_send_notif", notif, dev->desc->fw_name);
}
}
diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c
index 3c9d8efd3956..5fe3c572628b 100644
--- a/drivers/soc/qcom/wcd-dsp-glink.c
+++ b/drivers/soc/qcom/wcd-dsp-glink.c
@@ -58,6 +58,7 @@ struct wdsp_glink_rsp_que {
struct wdsp_glink_tx_buf {
struct work_struct tx_work;
+ struct work_struct free_tx_work;
/* Glink channel information */
struct wdsp_glink_ch *ch;
@@ -125,6 +126,46 @@ static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch);
static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch);
/*
+ * wdsp_glink_free_tx_buf_work - Work function to free tx pkt
+ * work: Work structure
+ */
+static void wdsp_glink_free_tx_buf_work(struct work_struct *work)
+{
+ struct wdsp_glink_tx_buf *tx_buf;
+
+ tx_buf = container_of(work, struct wdsp_glink_tx_buf,
+ free_tx_work);
+ vfree(tx_buf);
+}
+
+/*
+ * wdsp_glink_free_tx_buf - Function to free tx buffer
+ * priv: Pointer to the channel
+ * pkt_priv: Pointer to the tx buffer
+ */
+static void wdsp_glink_free_tx_buf(const void *priv, const void *pkt_priv)
+{
+ struct wdsp_glink_tx_buf *tx_buf = (struct wdsp_glink_tx_buf *)pkt_priv;
+ struct wdsp_glink_priv *wpriv;
+ struct wdsp_glink_ch *ch;
+
+ if (!priv) {
+ pr_err("%s: Invalid priv\n", __func__);
+ return;
+ }
+ if (!tx_buf) {
+ pr_err("%s: Invalid tx_buf\n", __func__);
+ return;
+ }
+
+ ch = (struct wdsp_glink_ch *)priv;
+ wpriv = ch->wpriv;
+ /* Work queue to free tx pkt */
+ INIT_WORK(&tx_buf->free_tx_work, wdsp_glink_free_tx_buf_work);
+ queue_work(wpriv->work_queue, &tx_buf->free_tx_work);
+}
+
+/*
* wdsp_glink_notify_rx - Glink notify rx callback for responses
* handle: Opaque Channel handle returned by GLink
* priv: Private pointer to the channel
@@ -183,14 +224,8 @@ static void wdsp_glink_notify_rx(void *handle, const void *priv,
static void wdsp_glink_notify_tx_done(void *handle, const void *priv,
const void *pkt_priv, const void *ptr)
{
- if (!pkt_priv) {
- pr_err("%s: Invalid parameter\n", __func__);
- return;
- }
- /* Free tx pkt */
- vfree(pkt_priv);
+ wdsp_glink_free_tx_buf(priv, pkt_priv);
}
-
/*
* wdsp_glink_notify_tx_abort - Glink notify tx abort callback to
* free tx buffer
@@ -201,12 +236,7 @@ static void wdsp_glink_notify_tx_done(void *handle, const void *priv,
static void wdsp_glink_notify_tx_abort(void *handle, const void *priv,
const void *pkt_priv)
{
- if (!pkt_priv) {
- pr_err("%s: Invalid parameter\n", __func__);
- return;
- }
- /* Free tx pkt */
- vfree(pkt_priv);
+ wdsp_glink_free_tx_buf(priv, pkt_priv);
}
/*
@@ -555,7 +585,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv,
goto done;
}
ch = kcalloc(no_of_channels, sizeof(struct wdsp_glink_ch *),
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!ch) {
ret = -ENOMEM;
goto done;
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 e72663bd2138..cdaf009c5b1f 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -540,7 +540,7 @@ static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr,
{
struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
int ret = 0;
- int val;
+ int val = 0;
u8 *reg_val = (u8 *)buf;
if (!swrm) {
@@ -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/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 548efd11e856..4499dd35f2dd 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1494,11 +1494,11 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag)
struct msm_spi *dd = spi_master_get_devdata(spi->master);
u32 spi_ioc;
u32 spi_ioc_orig;
- int rc;
+ int rc = 0;
rc = pm_runtime_get_sync(dd->dev);
if (rc < 0) {
- dev_err(dd->dev, "Failure during runtime get");
+ dev_err(dd->dev, "Failure during runtime get,rc=%d", rc);
return;
}
@@ -1513,6 +1513,15 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag)
if (!(spi->mode & SPI_CS_HIGH))
set_flag = !set_flag;
+ /* Serve only under mutex lock as RT suspend may cause a race */
+ mutex_lock(&dd->core_lock);
+ if (dd->suspended) {
+ dev_err(dd->dev, "%s: SPI operational state=%d Invalid\n",
+ __func__, dd->suspended);
+ mutex_unlock(&dd->core_lock);
+ return;
+ }
+
spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
spi_ioc_orig = spi_ioc;
if (set_flag)
@@ -1524,6 +1533,8 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag)
writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
if (dd->pdata->is_shared)
put_local_resources(dd);
+ mutex_unlock(&dd->core_lock);
+
pm_runtime_mark_last_busy(dd->dev);
pm_runtime_put_autosuspend(dd->dev);
}
@@ -2616,6 +2627,7 @@ static int msm_spi_pm_suspend_runtime(struct device *device)
wait_event_interruptible(dd->continue_suspend,
!dd->transfer_pending);
+ mutex_lock(&dd->core_lock);
if (dd->pdata && !dd->pdata->is_shared && dd->use_dma) {
msm_spi_bam_pipe_disconnect(dd, &dd->bam.prod);
msm_spi_bam_pipe_disconnect(dd, &dd->bam.cons);
@@ -2625,6 +2637,7 @@ static int msm_spi_pm_suspend_runtime(struct device *device)
if (dd->pdata)
msm_spi_clk_path_vote(dd, 0);
+ mutex_unlock(&dd->core_lock);
suspend_exit:
return 0;
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 205af6627b80..8deee007218b 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -146,7 +146,7 @@ int adjust_minadj(short *min_score_adj)
static int lmk_vmpressure_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
- int other_free, other_file;
+ int other_free = 0, other_file = 0;
unsigned long pressure = action;
int array_size = ARRAY_SIZE(lowmem_adj);
@@ -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/lustre/lustre/lov/lov_pack.c b/drivers/staging/lustre/lustre/lov/lov_pack.c
index 2fb1e974cc70..e11b1001d1f6 100644
--- a/drivers/staging/lustre/lustre/lov/lov_pack.c
+++ b/drivers/staging/lustre/lustre/lov/lov_pack.c
@@ -399,18 +399,10 @@ int lov_getstripe(struct obd_export *exp, struct lov_stripe_md *lsm,
struct lov_mds_md *lmmk = NULL;
int rc, lmm_size;
int lum_size;
- mm_segment_t seg;
if (!lsm)
return -ENODATA;
- /*
- * "Switch to kernel segment" to allow copying from kernel space by
- * copy_{to,from}_user().
- */
- seg = get_fs();
- set_fs(KERNEL_DS);
-
/* we only need the header part from user space to get lmm_magic and
* lmm_stripe_count, (the header part is common to v1 and v3) */
lum_size = sizeof(struct lov_user_md_v1);
@@ -485,6 +477,5 @@ int lov_getstripe(struct obd_export *exp, struct lov_stripe_md *lsm,
obd_free_diskmd(exp, &lmmk);
out_set:
- set_fs(seg);
return rc;
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_ap.c b/drivers/staging/rtl8188eu/core/rtw_ap.c
index 3cdb40fea5ee..f5cedbbc552a 100644
--- a/drivers/staging/rtl8188eu/core/rtw_ap.c
+++ b/drivers/staging/rtl8188eu/core/rtw_ap.c
@@ -894,7 +894,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len)
return _FAIL;
- if (len > MAX_IE_SZ)
+ if (len < 0 || len > MAX_IE_SZ)
return _FAIL;
pbss_network->IELength = len;
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
index e9c4f973bba9..7a8ceb961bb6 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);
@@ -1184,8 +1185,7 @@ void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc,
struct cb_desc *cb_desc, struct sk_buff *skb)
{
struct r8192_priv *priv = rtllib_priv(dev);
- dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len,
- PCI_DMA_TODEVICE);
+ dma_addr_t mapping;
struct tx_fwinfo_8190pci *pTxFwInfo = NULL;
pTxFwInfo = (struct tx_fwinfo_8190pci *)skb->data;
@@ -1196,8 +1196,6 @@ void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc,
pTxFwInfo->Short = _rtl92e_query_is_short(pTxFwInfo->TxHT,
pTxFwInfo->TxRate, cb_desc);
- if (pci_dma_mapping_error(priv->pdev, mapping))
- netdev_err(dev, "%s(): DMA Mapping error\n", __func__);
if (cb_desc->bAMPDUEnable) {
pTxFwInfo->AllowAggregation = 1;
pTxFwInfo->RxMF = cb_desc->ampdu_factor;
@@ -1232,6 +1230,14 @@ void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc,
}
memset((u8 *)pdesc, 0, 12);
+
+ mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pdev, mapping)) {
+ netdev_err(dev, "%s(): DMA Mapping error\n", __func__);
+ return;
+ }
+
pdesc->LINIP = 0;
pdesc->CmdInit = 1;
pdesc->Offset = sizeof(struct tx_fwinfo_8190pci) + 8;
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..60743bf27f37 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);
@@ -1154,15 +1154,28 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
if (cmd->unknown_data_length) {
cmd->data_length = size;
} else if (size != cmd->data_length) {
- pr_warn("TARGET_CORE[%s]: Expected Transfer Length:"
+ pr_warn_ratelimited("TARGET_CORE[%s]: Expected Transfer Length:"
" %u does not match SCSI CDB Length: %u for SAM Opcode:"
" 0x%02x\n", cmd->se_tfo->get_fabric_name(),
cmd->data_length, size, cmd->t_task_cdb[0]);
- if (cmd->data_direction == DMA_TO_DEVICE &&
- cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) {
- pr_err("Rejecting underflow/overflow WRITE data\n");
- return TCM_INVALID_CDB_FIELD;
+ if (cmd->data_direction == DMA_TO_DEVICE) {
+ if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) {
+ pr_err_ratelimited("Rejecting underflow/overflow"
+ " for WRITE data CDB\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+ /*
+ * Some fabric drivers like iscsi-target still expect to
+ * always reject overflow writes. Reject this case until
+ * full fabric driver level support for overflow writes
+ * is introduced tree-wide.
+ */
+ if (size > cmd->data_length) {
+ pr_err_ratelimited("Rejecting overflow for"
+ " WRITE control CDB\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
}
/*
* Reject READ_* or WRITE_* with overflow/underflow for
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
index d59b9736c570..5f955af4671a 100644
--- a/drivers/thermal/msm-tsens.c
+++ b/drivers/thermal/msm-tsens.c
@@ -2119,7 +2119,7 @@ static int get_device_tree_data(struct platform_device *pdev,
{
struct device_node *of_node = pdev->dev.of_node;
struct resource *res_mem = NULL;
- u32 *tsens_slope_data, *sensor_id, *client_id;
+ u32 *tsens_slope_data = NULL, *sensor_id, *client_id;
u32 *temp1_calib_offset_factor, *temp2_calib_offset_factor;
u32 rc = 0, i, tsens_num_sensors = 0;
u32 cycle_monitor = 0, wd_bark = 0;
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/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index c78406cb3325..2df1bf69e7c9 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.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
@@ -3292,40 +3292,55 @@ static int qpnp_adc_tm_remove(struct platform_device *pdev)
static void qpnp_adc_tm_shutdown(struct platform_device *pdev)
{
struct qpnp_adc_tm_chip *chip = dev_get_drvdata(&pdev->dev);
- int rc = 0;
+ int rc = 0, i = 0;
u8 reg_val = 0, status1 = 0, en_ctl1 = 0;
- /* Set measurement in single measurement mode */
- reg_val = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
- rc = qpnp_adc_tm_mode_select(chip, reg_val);
- if (rc < 0)
- pr_err("adc-tm single mode select failed\n");
+ if (!chip->adc_tm_hc) {
+ /* Set measurement in single measurement mode */
+ reg_val = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+ rc = qpnp_adc_tm_mode_select(chip, reg_val);
+ if (rc < 0)
+ pr_err("adc-tm single mode select failed\n");
+ }
/* Disable bank */
rc = qpnp_adc_tm_disable(chip);
if (rc < 0)
pr_err("adc-tm disable failed\n");
- /* Check if a conversion is in progress */
- rc = qpnp_adc_tm_req_sts_check(chip);
- if (rc < 0)
- pr_err("adc-tm req_sts check failed\n");
+ if (chip->adc_tm_hc) {
+ for (i = 0; i < 8; i++) {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(i),
+ QPNP_BTM_Mn_MEAS_EN, false);
+ if (rc < 0)
+ pr_err("multi meas disable failed\n");
+ }
+ } else {
+ /* Check if a conversion is in progress */
+ rc = qpnp_adc_tm_req_sts_check(chip);
+ if (rc < 0)
+ pr_err("adc-tm req_sts check failed\n");
- /* Disable multimeasurement */
- reg_val = 0;
- rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN, reg_val, 1);
- if (rc < 0)
- pr_err("adc-tm multi-measurement mode disable failed\n");
+ /* Disable multimeasurement */
+ reg_val = 0;
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_ADC_TM_MULTI_MEAS_EN, reg_val, 1);
+ if (rc < 0)
+ pr_err("adc-tm multi-meas mode disable failed\n");
- rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1);
- if (rc < 0)
- pr_err("adc-tm status1 read failed\n");
+ rc = qpnp_adc_tm_read_reg(chip,
+ QPNP_ADC_TM_STATUS1, &status1, 1);
+ if (rc < 0)
+ pr_err("adc-tm status1 read failed\n");
- rc = qpnp_adc_tm_read_reg(chip, QPNP_EN_CTL1, &en_ctl1, 1);
- if (rc < 0)
- pr_err("adc-tm en_ctl1 read failed\n");
+ rc = qpnp_adc_tm_read_reg(chip,
+ QPNP_EN_CTL1, &en_ctl1, 1);
+ if (rc < 0)
+ pr_err("adc-tm en_ctl1 read failed\n");
- pr_debug("adc-tm status1=0%x, en_ctl1=0x%x\n", status1, en_ctl1);
+ pr_debug("status1=0%x, en_ctl1=0x%x\n", status1, en_ctl1);
+ }
}
static int qpnp_adc_tm_suspend_noirq(struct device *dev)
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/efm32-uart.c b/drivers/tty/serial/efm32-uart.c
index 195acc868763..5d476916191b 100644
--- a/drivers/tty/serial/efm32-uart.c
+++ b/drivers/tty/serial/efm32-uart.c
@@ -27,6 +27,7 @@
#define UARTn_FRAME 0x04
#define UARTn_FRAME_DATABITS__MASK 0x000f
#define UARTn_FRAME_DATABITS(n) ((n) - 3)
+#define UARTn_FRAME_PARITY__MASK 0x0300
#define UARTn_FRAME_PARITY_NONE 0x0000
#define UARTn_FRAME_PARITY_EVEN 0x0200
#define UARTn_FRAME_PARITY_ODD 0x0300
@@ -572,12 +573,16 @@ static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port,
16 * (4 + (clkdiv >> 6)));
frame = efm32_uart_read32(efm_port, UARTn_FRAME);
- if (frame & UARTn_FRAME_PARITY_ODD)
+ switch (frame & UARTn_FRAME_PARITY__MASK) {
+ case UARTn_FRAME_PARITY_ODD:
*parity = 'o';
- else if (frame & UARTn_FRAME_PARITY_EVEN)
+ break;
+ case UARTn_FRAME_PARITY_EVEN:
*parity = 'e';
- else
+ break;
+ default:
*parity = 'n';
+ }
*bits = (frame & UARTn_FRAME_DATABITS__MASK) -
UARTn_FRAME_DATABITS(4) + 4;
diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c
index 88246f7e435a..0f23dda60011 100644
--- a/drivers/tty/serial/ifx6x60.c
+++ b/drivers/tty/serial/ifx6x60.c
@@ -1378,9 +1378,9 @@ static struct spi_driver ifx_spi_driver = {
static void __exit ifx_spi_exit(void)
{
/* unregister */
+ spi_unregister_driver(&ifx_spi_driver);
tty_unregister_driver(tty_drv);
put_tty_driver(tty_drv);
- spi_unregister_driver(&ifx_spi_driver);
unregister_reboot_notifier(&ifx_modem_reboot_notifier_block);
}
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/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 63a06ab6ba03..235e150d7b81 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1800,11 +1800,13 @@ static int sci_startup(struct uart_port *port)
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+ sci_request_dma(port);
+
ret = sci_request_irq(s);
- if (unlikely(ret < 0))
+ if (unlikely(ret < 0)) {
+ sci_free_dma(port);
return ret;
-
- sci_request_dma(port);
+ }
spin_lock_irqsave(&port->lock, flags);
sci_start_tx(port);
@@ -1834,8 +1836,8 @@ static void sci_shutdown(struct uart_port *port)
}
#endif
- sci_free_dma(port);
sci_free_irq(s);
+ sci_free_dma(port);
}
static unsigned int sci_scbrr_calc(struct sci_port *s, unsigned int bps,
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 7cef54334b12..1bb629ab8ecc 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -2070,13 +2070,12 @@ retry_open:
if (tty) {
mutex_unlock(&tty_mutex);
retval = tty_lock_interruptible(tty);
+ tty_kref_put(tty); /* drop kref from tty_driver_lookup_tty() */
if (retval) {
if (retval == -EINTR)
retval = -ERESTARTSYS;
goto err_unref;
}
- /* safe to drop the kref from tty_driver_lookup_tty() */
- tty_kref_put(tty);
retval = tty_reopen(tty);
if (retval < 0) {
tty_unlock(tty);
diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c
index d09293bc0e04..cff304abb619 100644
--- a/drivers/tty/tty_mutex.c
+++ b/drivers/tty/tty_mutex.c
@@ -24,10 +24,15 @@ EXPORT_SYMBOL(tty_lock);
int tty_lock_interruptible(struct tty_struct *tty)
{
+ int ret;
+
if (WARN(tty->magic != TTY_MAGIC, "L Bad %p\n", tty))
return -EIO;
tty_kref_get(tty);
- return mutex_lock_interruptible(&tty->legacy_mutex);
+ ret = mutex_lock_interruptible(&tty->legacy_mutex);
+ if (ret)
+ tty_kref_put(tty);
+ return ret;
}
void __lockfunc tty_unlock(struct tty_struct *tty)
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index 58c8485a0715..923379972707 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -295,7 +295,8 @@ static int ci_role_show(struct seq_file *s, void *data)
{
struct ci_hdrc *ci = s->private;
- seq_printf(s, "%s\n", ci_role(ci)->name);
+ if (ci->role != CI_ROLE_END)
+ seq_printf(s, "%s\n", ci_role(ci)->name);
return 0;
}
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index d8a045fc1fdb..aff086ca97e4 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1982,6 +1982,7 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
int ci_hdrc_gadget_init(struct ci_hdrc *ci)
{
struct ci_role_driver *rdrv;
+ int ret;
if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC))
return -ENXIO;
@@ -1994,7 +1995,10 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci)
rdrv->stop = udc_id_switch_for_host;
rdrv->irq = udc_irq;
rdrv->name = "gadget";
- ci->roles[CI_ROLE_GADGET] = rdrv;
- return udc_start(ci);
+ ret = udc_start(ci);
+ if (!ret)
+ ci->roles[CI_ROLE_GADGET] = rdrv;
+
+ return ret;
}
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/config.c b/drivers/usb/core/config.c
index 325cbc9c35d8..b1298f093f13 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -949,6 +949,14 @@ int usb_get_bos_descriptor(struct usb_device *dev)
dev->bos->ss_id =
(struct usb_ss_container_id_descriptor *)buffer;
break;
+ case USB_CAP_TYPE_CONFIG_SUMMARY:
+ /* one such desc per configuration */
+ if (!dev->bos->num_config_summary_desc)
+ dev->bos->config_summary =
+ (struct usb_config_summary_descriptor *)buffer;
+
+ dev->bos->num_config_summary_desc++;
+ break;
default:
break;
}
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/generic.c b/drivers/usb/core/generic.c
index 358ca8dd784f..72d1109f13eb 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -19,6 +19,8 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v3.h>
#include "usb.h"
static inline const char *plural(int n)
@@ -40,6 +42,36 @@ static int is_activesync(struct usb_interface_descriptor *desc)
&& desc->bInterfaceProtocol == 1;
}
+static int usb_audio_max_rev_config(struct usb_host_bos *bos)
+{
+ int desc_cnt, func_cnt, numfunc;
+ int num_cfg_desc;
+ struct usb_config_summary_descriptor *conf_summary;
+
+ if (!bos || !bos->config_summary)
+ goto done;
+
+ conf_summary = bos->config_summary;
+ num_cfg_desc = bos->num_config_summary_desc;
+
+ for (desc_cnt = 0; desc_cnt < num_cfg_desc; desc_cnt++) {
+ numfunc = conf_summary->bNumFunctions;
+ for (func_cnt = 0; func_cnt < numfunc; func_cnt++) {
+ /* look for BADD 3.0 */
+ if (conf_summary->cs_info[func_cnt].bClass ==
+ USB_CLASS_AUDIO &&
+ conf_summary->cs_info[func_cnt].bProtocol ==
+ UAC_VERSION_3 &&
+ conf_summary->cs_info[func_cnt].bSubClass !=
+ FULL_ADC_PROFILE)
+ return conf_summary->bConfigurationValue;
+ }
+ }
+
+done:
+ return -EINVAL;
+}
+
int usb_choose_configuration(struct usb_device *udev)
{
int i;
@@ -130,7 +162,6 @@ int usb_choose_configuration(struct usb_device *udev)
best = c;
break;
}
-
/* If all the remaining configs are vendor-specific,
* choose the first one. */
else if (!best)
@@ -143,7 +174,10 @@ int usb_choose_configuration(struct usb_device *udev)
insufficient_power, plural(insufficient_power));
if (best) {
- i = best->desc.bConfigurationValue;
+ /* choose usb audio class preferred config if available */
+ i = usb_audio_max_rev_config(udev->bos);
+ if (i < 0)
+ i = best->desc.bConfigurationValue;
dev_dbg(&udev->dev,
"configuration #%d chosen from %d choice%s\n",
i, num_configs, plural(num_configs));
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index aa00bb51940b..bc5a6966dda9 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2569,6 +2569,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
GFP_KERNEL);
if (!hcd->bandwidth_mutex) {
+ kfree(hcd->address0_mutex);
kfree(hcd);
dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");
return NULL;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 87912ead87b7..5de22f4892cd 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,13 +1323,19 @@ 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) {
message = "can't read hub descriptor";
goto fail;
- } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
+ }
+
+ maxchild = USB_MAXCHILDREN;
+ if (hub_is_superspeed(hdev))
+ maxchild = min_t(unsigned, maxchild, USB_SS_MAXPORTS);
+
+ if (hub->descriptor->bNbrPorts > maxchild) {
message = "hub has too many ports!";
ret = -ENODEV;
goto fail;
@@ -2079,6 +2097,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/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 2f1fb7e7aa54..9eba51b92f72 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -148,7 +148,8 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk");
if (IS_ERR(exynos->axius_clk)) {
dev_err(dev, "no AXI UpScaler clk specified\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto axius_clk_err;
}
clk_prepare_enable(exynos->axius_clk);
} else {
@@ -206,6 +207,7 @@ err3:
regulator_disable(exynos->vdd33);
err2:
clk_disable_unprepare(exynos->axius_clk);
+axius_clk_err:
clk_disable_unprepare(exynos->susp_clk);
clk_disable_unprepare(exynos->clk);
return ret;
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 4ed46d9ca279..c2d788bc4bc5 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -2671,12 +2671,16 @@ static int dwc3_msm_extcon_register(struct dwc3_msm *mdwc)
{
struct device_node *node = mdwc->dev->of_node;
struct extcon_dev *edev;
+ struct dwc3 *dwc;
int ret = 0;
+ dwc = platform_get_drvdata(mdwc->dwc3);
if (!of_property_read_bool(node, "extcon")) {
- if (usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST)
+ dev_dbg(mdwc->dev, "extcon property doesn't exist\n");
+ if (usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST
+ || dwc->is_drd)
return 0;
- dev_err(mdwc->dev, "extcon property doesn't exist\n");
+ dev_err(mdwc->dev, "Neither host nor DRD, fail probe\n");
return -EINVAL;
}
@@ -3137,8 +3141,9 @@ static int dwc3_msm_probe(struct platform_device *pdev)
device_create_file(&pdev->dev, &dev_attr_speed);
host_mode = usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST;
- if (!dwc->is_drd && host_mode) {
- dev_dbg(&pdev->dev, "DWC3 in host only mode\n");
+ if (host_mode ||
+ (dwc->is_drd && !of_property_read_bool(node, "extcon"))) {
+ dev_dbg(&pdev->dev, "DWC3 in default host mode\n");
mdwc->id_state = DWC3_ID_GROUND;
dwc3_ext_event_notify(mdwc);
}
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_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index 46be62b737f1..ec73b67096c5 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -776,11 +776,11 @@ static int audio_pcm_close(struct snd_pcm_substream *substream)
struct audio_dev *audio = substream->private_data;
unsigned long flags;
- spin_lock_irqsave(&audio->lock, flags);
-
/* Remove the QoS request */
pm_qos_remove_request(&audio->pm_qos);
+ spin_lock_irqsave(&audio->lock, flags);
+
audio->substream = NULL;
spin_unlock_irqrestore(&audio->lock, flags);
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_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 7216fdd4245d..3f903d4776b4 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -1035,7 +1035,7 @@ gsi_ctrl_dev_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
log_event_dbg("%s: cpkt size:%d", __func__, cpkt->len);
if (qti_packet_debug)
print_hex_dump(KERN_DEBUG, "READ:", DUMP_PREFIX_OFFSET, 16, 1,
- buf, min_t(int, 30, cpkt->len), false);
+ cpkt->buf, min_t(int, 30, cpkt->len), false);
ret = copy_to_user(buf, cpkt->buf, cpkt->len);
if (ret) {
@@ -1108,7 +1108,7 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf,
c_port->copied_from_modem++;
if (qti_packet_debug)
print_hex_dump(KERN_DEBUG, "WRITE:", DUMP_PREFIX_OFFSET, 16, 1,
- buf, min_t(int, 30, count), false);
+ cpkt->buf, min_t(int, 30, count), false);
spin_lock_irqsave(&c_port->lock, flags);
list_add_tail(&cpkt->list, &c_port->cpkt_resp_q);
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 59d6ac67d072..9b7274821d0b 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -399,7 +399,11 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
/* Caller must hold fsg->lock */
static void wakeup_thread(struct fsg_common *common)
{
- smp_wmb(); /* ensure the write of bh->state is complete */
+ /*
+ * Ensure the reading of thread_wakeup_needed
+ * and the writing of bh->state are completed
+ */
+ smp_mb();
/* Tell the main thread that something has happened */
common->thread_wakeup_needed = 1;
if (common->thread_task)
@@ -649,7 +653,12 @@ static int sleep_thread(struct fsg_common *common, bool can_freeze)
}
__set_current_state(TASK_RUNNING);
common->thread_wakeup_needed = 0;
- smp_rmb(); /* ensure the latest bh->state is visible */
+
+ /*
+ * Ensure the writing of thread_wakeup_needed
+ * and the reading of bh->state are completed
+ */
+ smp_mb();
return rc;
}
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/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index de014436fb22..43ce2cfcdb4d 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1676,9 +1676,10 @@ static void
gadgetfs_suspend (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
+ unsigned long flags;
INFO (dev, "suspended from state %d\n", dev->state);
- spin_lock (&dev->lock);
+ spin_lock_irqsave(&dev->lock, flags);
switch (dev->state) {
case STATE_DEV_SETUP: // VERY odd... host died??
case STATE_DEV_CONNECTED:
@@ -1689,7 +1690,7 @@ gadgetfs_suspend (struct usb_gadget *gadget)
default:
break;
}
- spin_unlock (&dev->lock);
+ spin_unlock_irqrestore(&dev->lock, flags);
}
static struct usb_gadget_driver gadgetfs_driver = {
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 6610f7a023d3..64f404a1a072 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -442,23 +442,16 @@ static void set_link_state(struct dummy_hcd *dum_hcd)
/* Report reset and disconnect events to the driver */
if (dum->driver && (disconnect || reset)) {
stop_activity(dum);
- spin_unlock(&dum->lock);
if (reset)
usb_gadget_udc_reset(&dum->gadget, dum->driver);
else
dum->driver->disconnect(&dum->gadget);
- spin_lock(&dum->lock);
}
} else if (dum_hcd->active != dum_hcd->old_active) {
- if (dum_hcd->old_active && dum->driver->suspend) {
- spin_unlock(&dum->lock);
+ if (dum_hcd->old_active && dum->driver->suspend)
dum->driver->suspend(&dum->gadget);
- spin_lock(&dum->lock);
- } else if (!dum_hcd->old_active && dum->driver->resume) {
- spin_unlock(&dum->lock);
+ else if (!dum_hcd->old_active && dum->driver->resume)
dum->driver->resume(&dum->gadget);
- spin_lock(&dum->lock);
- }
}
dum_hcd->old_status = dum_hcd->port_status;
@@ -985,7 +978,9 @@ static int dummy_udc_stop(struct usb_gadget *g)
struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
struct dummy *dum = dum_hcd->dum;
+ spin_lock_irq(&dum->lock);
dum->driver = NULL;
+ spin_unlock_irq(&dum->lock);
return 0;
}
@@ -2011,7 +2006,7 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = 1;
desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
- desc->u.ss.DeviceRemovable = 0xffff;
+ desc->u.ss.DeviceRemovable = 0;
}
static inline void hub_descriptor(struct usb_hub_descriptor *desc)
@@ -2023,8 +2018,8 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
HUB_CHAR_INDV_PORT_LPSM |
HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = 1;
- desc->u.hs.DeviceRemovable[0] = 0xff;
- desc->u.hs.DeviceRemovable[1] = 0xff;
+ desc->u.hs.DeviceRemovable[0] = 0;
+ desc->u.hs.DeviceRemovable[1] = 0xff; /* PortPwrCtrlMask */
}
static int dummy_hub_control(
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index 6706aef907f4..a47de8c31ce9 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -2425,11 +2425,8 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
nuke(&dev->ep[i]);
/* report disconnect; the driver is already quiesced */
- if (driver) {
- spin_unlock(&dev->lock);
+ if (driver)
driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
- }
usb_reinit(dev);
}
@@ -3275,8 +3272,6 @@ next_endpoints:
BIT(PCI_RETRY_ABORT_INTERRUPT))
static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
-__releases(dev->lock)
-__acquires(dev->lock)
{
struct net2280_ep *ep;
u32 tmp, num, mask, scratch;
@@ -3317,14 +3312,12 @@ __acquires(dev->lock)
if (disconnect || reset) {
stop_activity(dev, dev->driver);
ep0_start(dev);
- spin_unlock(&dev->lock);
if (reset)
usb_gadget_udc_reset
(&dev->gadget, dev->driver);
else
(dev->driver->disconnect)
(&dev->gadget);
- spin_lock(&dev->lock);
return;
}
}
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 4cbd0633c5c2..a11c2c8bda53 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -1269,7 +1269,7 @@ static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
time = 30;
break;
default:
- time = 300;
+ time = 50;
break;
}
@@ -1785,6 +1785,7 @@ static void r8a66597_td_timer(unsigned long _r8a66597)
pipe = td->pipe;
pipe_stop(r8a66597, pipe);
+ /* Select a different address or endpoint */
new_td = td;
do {
list_move_tail(&new_td->queue,
@@ -1794,7 +1795,8 @@ static void r8a66597_td_timer(unsigned long _r8a66597)
new_td = td;
break;
}
- } while (td != new_td && td->address == new_td->address);
+ } while (td != new_td && td->address == new_td->address &&
+ td->pipe->info.epnum == new_td->pipe->info.epnum);
start_transfer(r8a66597, new_td);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index d885033d3322..9dbd7595a7a3 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -376,10 +376,6 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
int i;
ret = 0;
- virt_dev = xhci->devs[slot_id];
- if (!virt_dev)
- return -ENODEV;
-
cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
if (!cmd) {
xhci_dbg(xhci, "Couldn't allocate command structure.\n");
@@ -387,6 +383,13 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
}
spin_lock_irqsave(&xhci->lock, flags);
+ virt_dev = xhci->devs[slot_id];
+ if (!virt_dev) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_free_command(xhci, cmd);
+ return -ENODEV;
+ }
+
for (i = LAST_EP_INDEX; i > 0; i--) {
if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) {
struct xhci_command *command;
@@ -403,6 +406,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
i, suspend);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_free_command(xhci, command);
goto err_cmd_queue;
}
}
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..e8f990642281 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 &&
@@ -195,6 +198,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
pdev->device == 0x1042)
xhci->quirks |= XHCI_BROKEN_STREAMS;
+ if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
+ pdev->device == 0x1142)
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
if (xhci->quirks & XHCI_RESET_ON_RESUME)
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
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 7bfadccf0e47..03aeec2e878c 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -29,6 +29,15 @@
#include <linux/usb/usbpd.h>
#include "usbpd.h"
+/* To start USB stack for USB3.1 complaince testing */
+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,
@@ -187,6 +196,8 @@ static void *usbpd_ipc_log;
#define PD_MAX_MSG_ID 7
+#define PD_MAX_DATA_OBJ 7
+
#define PD_MSG_HDR(type, dr, pr, id, cnt, rev) \
(((type) & 0xF) | ((dr) << 5) | (rev << 6) | \
((pr) << 8) | ((id) << 9) | ((cnt) << 12))
@@ -308,7 +319,7 @@ struct usbpd {
struct list_head rx_q;
spinlock_t rx_lock;
- u32 received_pdos[7];
+ u32 received_pdos[PD_MAX_DATA_OBJ];
u16 src_cap_id;
u8 selected_pdo;
u8 requested_pdo;
@@ -481,13 +492,12 @@ static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data,
ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), type, 15);
/* TODO figure out timeout. based on tReceive=1.1ms x nRetryCount? */
- /* MessageID incremented regardless of Tx error */
- pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID;
-
if (ret < 0)
return ret;
else if (ret != num_data * sizeof(u32))
return -EIO;
+
+ pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID;
return 0;
}
@@ -547,6 +557,7 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua)
static int pd_eval_src_caps(struct usbpd *pd)
{
+ int obj_cnt;
union power_supply_propval val;
u32 first_pdo = pd->received_pdos[0];
@@ -563,6 +574,13 @@ static int pd_eval_src_caps(struct usbpd *pd)
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val);
+ for (obj_cnt = 1; obj_cnt < PD_MAX_DATA_OBJ; obj_cnt++) {
+ if ((PD_SRC_PDO_TYPE(pd->received_pdos[obj_cnt]) ==
+ PD_SRC_PDO_TYPE_AUGMENTED) &&
+ !PD_APDO_PPS(pd->received_pdos[obj_cnt]))
+ pd->spec_rev = USBPD_REV_30;
+ }
+
/* Select the first PDO (vSafe5V) immediately. */
pd_select_pdo(pd, 1, 0, 0);
@@ -571,6 +589,8 @@ static int pd_eval_src_caps(struct usbpd *pd)
static void pd_send_hard_reset(struct usbpd *pd)
{
+ union power_supply_propval val = {0};
+
usbpd_dbg(&pd->dev, "send hard reset");
/* Force CC logic to source/sink to keep Rp/Rd unchanged */
@@ -578,6 +598,7 @@ static void pd_send_hard_reset(struct usbpd *pd)
pd->hard_reset_count++;
pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */
pd->in_pr_swap = false;
+ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val);
}
static void kick_sm(struct usbpd *pd, int ms)
@@ -593,6 +614,8 @@ static void kick_sm(struct usbpd *pd, int ms)
static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
{
+ union power_supply_propval val = {1};
+
if (type != HARD_RESET_SIG) {
usbpd_err(&pd->dev, "invalid signal (%d) received\n", type);
return;
@@ -603,6 +626,9 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
/* Force CC logic to source/sink to keep Rp/Rd unchanged */
set_power_role(pd, pd->current_pr);
pd->hard_reset_recvd = true;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
kick_sm(pd, 0);
}
@@ -653,12 +679,6 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
return;
}
- /* if spec rev differs (i.e. is older), update PHY */
- if (PD_MSG_HDR_REV(header) < pd->spec_rev) {
- pd->spec_rev = PD_MSG_HDR_REV(header);
- pd_phy_update_spec_rev(pd->spec_rev);
- }
-
rx_msg = kzalloc(sizeof(*rx_msg), GFP_KERNEL);
if (!rx_msg)
return;
@@ -701,7 +721,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
.shutdown_cb = phy_shutdown,
.frame_filter_val = FRAME_FILTER_EN_SOP |
FRAME_FILTER_EN_HARD_RESET,
- .spec_rev = USBPD_REV_20,
};
union power_supply_propval val = {0};
unsigned long flags;
@@ -748,8 +767,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);
- /* support only PD 2.0 as a source */
- pd->spec_rev = USBPD_REV_20;
pd_reset_protocol(pd);
if (!pd->in_pr_swap) {
@@ -760,7 +777,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
phy_params.data_role = pd->current_dr;
phy_params.power_role = pd->current_pr;
- phy_params.spec_rev = pd->spec_rev;
ret = pd_phy_open(&phy_params);
if (ret) {
@@ -772,14 +788,15 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
pd->pd_phy_opened = true;
- } else {
- pd_phy_update_spec_rev(pd->spec_rev);
}
pd->current_state = PE_SRC_SEND_CAPABILITIES;
if (pd->in_pr_swap) {
kick_sm(pd, SWAP_SOURCE_START_TIME);
pd->in_pr_swap = false;
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PR_SWAP, &val);
break;
}
@@ -892,7 +909,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->current_dr = DR_UFP;
if (pd->psy_type == POWER_SUPPLY_TYPE_USB ||
- pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP)
+ pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP ||
+ usb_compliance_mode)
start_usb_peripheral(pd);
}
@@ -906,14 +924,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
break;
}
- if (!val.intval)
+ if (!val.intval || disable_usb_pd)
break;
- /*
- * support up to PD 3.0 as a sink; if source is 2.0,
- * phy_msg_received() will handle the downgrade.
- */
- pd->spec_rev = USBPD_REV_30;
pd_reset_protocol(pd);
if (!pd->in_pr_swap) {
@@ -924,7 +937,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
phy_params.data_role = pd->current_dr;
phy_params.power_role = pd->current_pr;
- phy_params.spec_rev = pd->spec_rev;
ret = pd_phy_open(&phy_params);
if (ret) {
@@ -936,8 +948,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
pd->pd_phy_opened = true;
- } else {
- pd_phy_update_spec_rev(pd->spec_rev);
}
pd->current_voltage = pd->requested_voltage = 5000000;
@@ -1576,7 +1586,6 @@ static void usbpd_sm(struct work_struct *w)
memset(&pd->received_pdos, 0, sizeof(pd->received_pdos));
rx_msg_cleanup(pd);
- val.intval = 0;
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
@@ -1607,6 +1616,10 @@ static void usbpd_sm(struct work_struct *w)
usleep_range(ERROR_RECOVERY_TIME * USEC_PER_MSEC,
(ERROR_RECOVERY_TIME + 5) * USEC_PER_MSEC);
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PR_SWAP, &val);
+
/* set due to dual_role class "mode" change */
if (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE)
val.intval = pd->forced_pr;
@@ -1630,11 +1643,22 @@ static void usbpd_sm(struct work_struct *w)
if (pd->hard_reset_recvd) {
pd->hard_reset_recvd = false;
- val.intval = 1;
+ if (pd->requested_current) {
+ val.intval = pd->requested_current = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
+ }
+
+ pd->requested_voltage = 5000000;
+ val.intval = pd->requested_voltage;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+ POWER_SUPPLY_PROP_VOLTAGE_MIN, &val);
pd->in_pr_swap = false;
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PR_SWAP, &val);
+
pd->in_explicit_contract = false;
pd->selected_pdo = pd->requested_pdo = 0;
pd->rdo = 0;
@@ -1895,6 +1919,9 @@ static void usbpd_sm(struct work_struct *w)
case PE_SNK_WAIT_FOR_CAPABILITIES:
pd->in_pr_swap = false;
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PR_SWAP, &val);
if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) {
val.intval = 0;
@@ -1914,15 +1941,6 @@ static void usbpd_sm(struct work_struct *w)
POWER_SUPPLY_PROP_PD_ACTIVE, &val);
} else if (pd->hard_reset_count < 3) {
usbpd_set_state(pd, PE_SNK_HARD_RESET);
- } else if (pd->pd_connected) {
- usbpd_info(&pd->dev, "Sink hard reset count exceeded, forcing reconnect\n");
-
- val.intval = 0;
- power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
- &val);
-
- usbpd_set_state(pd, PE_ERROR_RECOVERY);
} else {
usbpd_dbg(&pd->dev, "Sink hard reset count exceeded, disabling PD\n");
@@ -2073,6 +2091,9 @@ static void usbpd_sm(struct work_struct *w)
}
pd->in_pr_swap = true;
+ val.intval = 1;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PR_SWAP, &val);
usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
break;
} else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) {
@@ -2216,6 +2237,9 @@ static void usbpd_sm(struct work_struct *w)
case PE_PRS_SRC_SNK_TRANSITION_TO_OFF:
pd->in_pr_swap = true;
+ val.intval = 1;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PR_SWAP, &val);
pd->in_explicit_contract = false;
if (pd->vbus_enabled) {
@@ -2256,6 +2280,9 @@ static void usbpd_sm(struct work_struct *w)
}
pd->in_pr_swap = true;
+ val.intval = 1;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PR_SWAP, &val);
usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
break;
@@ -2529,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;
@@ -2555,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:
@@ -3314,6 +3348,8 @@ struct usbpd *usbpd_create(struct device *parent)
pd->dual_role->drv_data = pd;
}
+ /* default support as PD 2.0 source or sink */
+ pd->spec_rev = USBPD_REV_20;
pd->current_pr = PR_NONE;
pd->current_dr = DR_NONE;
list_add_tail(&pd->instance, &_usbpd);
diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c
index 588af94db6cd..e200c25bc23a 100644
--- a/drivers/usb/pd/qpnp-pdphy.c
+++ b/drivers/usb/pd/qpnp-pdphy.c
@@ -108,6 +108,7 @@ struct usb_pdphy {
int tx_status;
u8 frame_filter_val;
bool in_test_data_mode;
+ bool rx_busy;
enum data_role data_role;
enum power_role power_role;
@@ -334,15 +335,6 @@ int pd_phy_update_roles(enum data_role dr, enum power_role pr)
}
EXPORT_SYMBOL(pd_phy_update_roles);
-int pd_phy_update_spec_rev(enum pd_spec_rev rev)
-{
- struct usb_pdphy *pdphy = __pdphy;
-
- return pdphy_masked_write(pdphy, USB_PDPHY_MSG_CONFIG,
- MSG_CONFIG_SPEC_REV_MASK, rev);
-}
-EXPORT_SYMBOL(pd_phy_update_spec_rev);
-
int pd_phy_open(struct pd_phy_params *params)
{
int ret;
@@ -377,7 +369,9 @@ int pd_phy_open(struct pd_phy_params *params)
if (ret)
return ret;
- ret = pd_phy_update_spec_rev(params->spec_rev);
+ /* PD 2.0 phy */
+ ret = pdphy_masked_write(pdphy, USB_PDPHY_MSG_CONFIG,
+ MSG_CONFIG_SPEC_REV_MASK, USBPD_REV_20);
if (ret)
return ret;
@@ -492,6 +486,12 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
return -EINVAL;
}
+ ret = pdphy_reg_read(pdphy, &val, USB_PDPHY_RX_ACKNOWLEDGE, 1);
+ if (ret || val || pdphy->rx_busy) {
+ dev_err(pdphy->dev, "%s: RX message pending\n", __func__);
+ return -EBUSY;
+ }
+
pdphy->tx_status = -EINPROGRESS;
/* write 2 byte SOP message header */
@@ -664,6 +664,15 @@ static int pd_phy_bist_mode(u8 bist_mode)
BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE);
}
+static irqreturn_t pdphy_msg_rx_irq(int irq, void *data)
+{
+ struct usb_pdphy *pdphy = data;
+
+ pdphy->rx_busy = true;
+
+ return IRQ_WAKE_THREAD;
+}
+
static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data)
{
u8 size, rx_status, frame_type;
@@ -720,6 +729,7 @@ static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data)
false);
pdphy->rx_bytes += size + 1;
done:
+ pdphy->rx_busy = false;
return IRQ_HANDLED;
}
@@ -805,7 +815,7 @@ static int pdphy_probe(struct platform_device *pdev)
return ret;
ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
- &pdphy->msg_rx_irq, "msg-rx", NULL,
+ &pdphy->msg_rx_irq, "msg-rx", pdphy_msg_rx_irq,
pdphy_msg_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
if (ret < 0)
return ret;
diff --git a/drivers/usb/pd/usbpd.h b/drivers/usb/pd/usbpd.h
index b2663add7f3c..108701739f89 100644
--- a/drivers/usb/pd/usbpd.h
+++ b/drivers/usb/pd/usbpd.h
@@ -68,7 +68,6 @@ struct pd_phy_params {
enum data_role data_role;
enum power_role power_role;
u8 frame_filter_val;
- u8 spec_rev;
};
#if IS_ENABLED(CONFIG_QPNP_USB_PDPHY)
@@ -77,7 +76,6 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms);
int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
enum pd_msg_type type, unsigned int timeout_ms);
int pd_phy_update_roles(enum data_role dr, enum power_role pr);
-int pd_phy_update_spec_rev(enum pd_spec_rev rev);
void pd_phy_close(void);
#else
static inline int pd_phy_open(struct pd_phy_params *params)
@@ -101,11 +99,6 @@ static inline int pd_phy_update_roles(enum data_role dr, enum power_role pr)
return -ENODEV;
}
-static inline int pd_phy_update_spec_rev(enum pd_spec_rev rev)
-{
- return -ENODEV;
-}
-
static inline void pd_phy_close(void)
{
}
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/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index c082ae8e50ce..2a708dd2ecee 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1097,6 +1097,13 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
void __user *argp = (void __user *)arg;
long ret = 0;
+ memset(&var, 0, sizeof(var));
+ memset(&fix, 0, sizeof(fix));
+ memset(&con2fb, 0, sizeof(con2fb));
+ memset(&cmap_from, 0, sizeof(cmap_from));
+ memset(&cmap, 0, sizeof(cmap));
+ memset(&event, 0, sizeof(event));
+
switch (cmd) {
case FBIOGET_VSCREENINFO:
if (!lock_fb_info(info))
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_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c
index 3330f8f62b78..c6ff92ed1686 100644
--- a/drivers/video/fbdev/msm/mdss_dba_utils.c
+++ b/drivers/video/fbdev/msm/mdss_dba_utils.c
@@ -576,7 +576,6 @@ int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo)
video_cfg.h_pulse_width = pinfo->lcdc.h_pulse_width;
video_cfg.v_pulse_width = pinfo->lcdc.v_pulse_width;
video_cfg.pclk_khz = (unsigned long)pinfo->clk_rate / 1000;
- video_cfg.hdmi_mode = !hdmi_edid_is_dvi_mode(ud->edid_data);
/* Calculate number of DSI lanes configured */
video_cfg.num_of_input_lanes = 0;
@@ -592,6 +591,8 @@ int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo)
/* Get scan information from EDID */
video_cfg.vic = mdss_dba_get_vic_panel_info(ud, pinfo);
ud->current_vic = video_cfg.vic;
+ video_cfg.hdmi_mode = hdmi_edid_get_sink_mode(ud->edid_data,
+ video_cfg.vic);
video_cfg.scaninfo = hdmi_edid_get_sink_scaninfo(ud->edid_data,
video_cfg.vic);
if (ud->ops.video_on)
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..c8afb3a244c3 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);
}
@@ -1674,6 +1674,8 @@ exit:
int mdss_dp_on(struct mdss_panel_data *pdata)
{
struct mdss_dp_drv_pdata *dp_drv = NULL;
+ bool hpd;
+ int rc = 0;
if (!pdata) {
pr_err("Invalid input data\n");
@@ -1683,6 +1685,22 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
dp_drv = container_of(pdata, struct mdss_dp_drv_pdata,
panel_data);
+ mutex_lock(&dp_drv->attention_lock);
+ hpd = dp_drv->cable_connected;
+ mutex_unlock(&dp_drv->attention_lock);
+
+ /* In case of device coming out of PM_SUSPEND, there can be
+ * a corner case where the sink is turned off or the DP cable
+ * is disconnected almost at the same time as userspace triggering
+ * unblank. This can cause the UNBLANK call to be still triggered
+ * before the disconnect event is notified to the userspace.
+ * Avoid turning ON DP path in such cases.
+ */
+ if (!hpd || !dp_drv->alt_mode.dp_status.hpd_high) {
+ pr_err("DP sink not connected\n");
+ return -EINVAL;
+ }
+
/*
* If the link already active, then nothing needs to be done here.
* However, it is possible that the the power_on flag could be
@@ -1707,15 +1725,18 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
* init/deinit during unrelated resume/suspend events,
* add host initialization call before DP power-on.
*/
- if (!dp_drv->dp_initialized)
- mdss_dp_host_init(pdata);
+ if (!dp_drv->dp_initialized) {
+ rc = mdss_dp_host_init(pdata);
+ if (rc < 0)
+ return rc;
+ }
return mdss_dp_on_hpd(dp_drv);
}
static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp)
{
- return dp->dpcd.downstream_port.dfp_present;
+ return dp->dpcd.downstream_port.dsp_present;
}
static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp)
@@ -1941,6 +1962,11 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
}
dp_drv->orientation = usbpd_get_plug_orientation(dp_drv->pd);
+ if (dp_drv->orientation == ORIENTATION_NONE) {
+ pr_err("DP cable might be disconnected\n");
+ ret = -EINVAL;
+ goto orientation_error;
+ }
dp_drv->aux_sel_gpio_output = 0;
if (dp_drv->orientation == ORIENTATION_CC2)
@@ -1981,8 +2007,9 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
return 0;
clk_error:
- mdss_dp_regulator_ctrl(dp_drv, false);
mdss_dp_config_gpios(dp_drv, false);
+orientation_error:
+ mdss_dp_regulator_ctrl(dp_drv, false);
vreg_error:
return ret;
}
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 0d9cf7b72b4d..f7b3d4664e86 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -311,7 +311,7 @@ static void mdss_dp_calc_tu_parameters(u8 link_rate, u8 ln_cnt,
bool n_err_neg, nn_err_neg;
u8 hblank_margin = 16;
- u8 tu_size, tu_size_desired, tu_size_minus1;
+ u8 tu_size, tu_size_desired = 0, tu_size_minus1;
int valid_boundary_link;
u64 resulting_valid;
u64 total_valid;
@@ -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 d5caf0e6bb1c..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__);
@@ -2091,7 +2096,7 @@ static int __mdss_dsi_dfps_update_clks(struct mdss_panel_data *pdata,
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
struct mdss_dsi_ctrl_pdata *sctrl_pdata = NULL;
- struct mdss_panel_info *pinfo, *spinfo;
+ struct mdss_panel_info *pinfo, *spinfo = NULL;
int rc = 0;
if (pdata == NULL) {
@@ -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));
}
@@ -4301,6 +4307,15 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev,
of_property_read_bool(ctrl_pdev->dev.of_node,
"qcom,platform-bklight-en-gpio-invert");
+ ctrl_pdata->avdd_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,platform-avdd-en-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->avdd_en_gpio))
+ pr_info("%s: avdd_en gpio not specified\n", __func__);
+
+ ctrl_pdata->avdd_en_gpio_invert =
+ of_property_read_bool(ctrl_pdev->dev.of_node,
+ "qcom,platform-avdd-en-gpio-invert");
+
ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
"qcom,platform-reset-gpio", 0);
if (!gpio_is_valid(ctrl_pdata->rst_gpio))
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 62d88f0af652..9847016fed29 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -455,8 +455,11 @@ struct mdss_dsi_ctrl_pdata {
int bklt_en_gpio;
bool bklt_en_gpio_invert;
bool bklt_en_gpio_state;
+ int avdd_en_gpio;
+ 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 f0fb791a7b8d..c766ff983045 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -1161,6 +1161,8 @@ static int mdss_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl)
rc = mdss_dsi_cmdlist_put(ctrl, &cmdreq);
if (rc <= 0) {
+ if (!mdss_dsi_sync_wait_enable(ctrl) ||
+ mdss_dsi_sync_wait_trigger(ctrl))
pr_err("%s: get status: fail\n", __func__);
return rc;
}
@@ -2273,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;
@@ -2333,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 60012c71449c..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);
}
@@ -260,6 +265,15 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
rc);
goto rst_gpio_err;
}
+ if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) {
+ rc = gpio_request(ctrl_pdata->avdd_en_gpio,
+ "avdd_enable");
+ if (rc) {
+ pr_err("request avdd_en gpio failed, rc=%d\n",
+ rc);
+ goto avdd_en_gpio_err;
+ }
+ }
if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) {
rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel");
if (rc) {
@@ -272,6 +286,9 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
return rc;
lcd_mode_sel_gpio_err:
+ if (gpio_is_valid(ctrl_pdata->avdd_en_gpio))
+ gpio_free(ctrl_pdata->avdd_en_gpio);
+avdd_en_gpio_err:
gpio_free(ctrl_pdata->rst_gpio);
rst_gpio_err:
if (gpio_is_valid(ctrl_pdata->disp_en_gpio))
@@ -424,6 +441,21 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
if (pdata->panel_info.rst_seq[++i])
usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000);
}
+
+ if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) {
+ if (ctrl_pdata->avdd_en_gpio_invert) {
+ rc = gpio_direction_output(
+ ctrl_pdata->avdd_en_gpio, 0);
+ } else {
+ rc = gpio_direction_output(
+ ctrl_pdata->avdd_en_gpio, 1);
+ }
+ if (rc) {
+ pr_err("%s: unable to set dir for avdd_en gpio\n",
+ __func__);
+ goto exit;
+ }
+ }
}
if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) {
@@ -452,6 +484,14 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
pr_debug("%s: Reset panel done\n", __func__);
}
} else {
+ if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) {
+ if (ctrl_pdata->avdd_en_gpio_invert)
+ gpio_set_value((ctrl_pdata->avdd_en_gpio), 1);
+ else
+ gpio_set_value((ctrl_pdata->avdd_en_gpio), 0);
+
+ gpio_free(ctrl_pdata->avdd_en_gpio);
+ }
if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) {
gpio_set_value((ctrl_pdata->disp_en_gpio), 0);
gpio_free(ctrl_pdata->disp_en_gpio);
@@ -2378,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.c b/drivers/video/fbdev/msm/mdss_fb.c
index 40a79c4af38e..fe1289633291 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -1192,11 +1192,24 @@ static int mdss_fb_init_panel_modes(struct msm_fb_data_type *mfd,
if (pdata->next) {
spt = mdss_panel_get_timing_by_name(pdata->next,
modedb[i].name);
- if (!IS_ERR_OR_NULL(spt))
+ /* for split config, recalculate xres and pixel clock */
+ if (!IS_ERR_OR_NULL(spt)) {
+ unsigned long pclk, h_total, v_total;
modedb[i].xres += spt->xres;
- else
+ h_total = modedb[i].xres +
+ modedb[i].left_margin +
+ modedb[i].right_margin +
+ modedb[i].hsync_len;
+ v_total = modedb[i].yres +
+ modedb[i].lower_margin +
+ modedb[i].upper_margin +
+ modedb[i].vsync_len;
+ pclk = h_total * v_total * modedb[i].refresh;
+ modedb[i].pixclock = KHZ2PICOS(pclk / 1000);
+ } else {
pr_debug("no matching split config for %s\n",
modedb[i].name);
+ }
/*
* if no panel timing found for current, need to
@@ -3607,6 +3620,16 @@ static void mdss_fb_var_to_panelinfo(struct fb_var_screeninfo *var,
*/
if (pinfo->is_dba_panel)
pinfo->mipi.dsi_pclk_rate = pinfo->clk_rate;
+
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ pinfo->lcdc.h_polarity = 0;
+ else
+ pinfo->lcdc.h_polarity = 1;
+
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ pinfo->lcdc.v_polarity = 0;
+ else
+ pinfo->lcdc.v_polarity = 1;
}
void mdss_panelinfo_to_fb_var(struct mdss_panel_info *pinfo,
@@ -4911,6 +4934,15 @@ static int __ioctl_wait_idle(struct msm_fb_data_type *mfd, u32 cmd)
return ret;
}
+static bool check_not_supported_ioctl(u32 cmd)
+{
+ return((cmd == MSMFB_OVERLAY_SET) || (cmd == MSMFB_OVERLAY_UNSET) ||
+ (cmd == MSMFB_OVERLAY_GET) || (cmd == MSMFB_OVERLAY_PREPARE) ||
+ (cmd == MSMFB_DISPLAY_COMMIT) || (cmd == MSMFB_OVERLAY_PLAY) ||
+ (cmd == MSMFB_BUFFER_SYNC) || (cmd == MSMFB_OVERLAY_QUEUE) ||
+ (cmd == MSMFB_NOTIFY_UPDATE));
+}
+
/*
* mdss_fb_do_ioctl() - MDSS Framebuffer ioctl function
* @info: pointer to framebuffer info
@@ -4945,6 +4977,11 @@ int mdss_fb_do_ioctl(struct fb_info *info, unsigned int cmd,
if (!pdata || pdata->panel_info.dynamic_switch_pending)
return -EPERM;
+ if (check_not_supported_ioctl(cmd)) {
+ pr_err("Unsupported ioctl\n");
+ return -EINVAL;
+ }
+
atomic_inc(&mfd->ioctl_ref_cnt);
mdss_fb_power_setting_idle(mfd);
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 102c22cba7dd..a953dd1a2ac2 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -62,11 +62,6 @@
#define EDID_VENDOR_ID_SIZE 4
#define EDID_IEEE_REG_ID 0x0c03
-enum edid_sink_mode {
- SINK_MODE_DVI,
- SINK_MODE_HDMI
-};
-
enum luminance_value {
NO_LUMINANCE_DATA = 3,
MAXIMUM_LUMINANCE = 4,
@@ -474,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__);
@@ -488,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;
@@ -2406,7 +2418,7 @@ end:
return scaninfo;
} /* hdmi_edid_get_sink_scaninfo */
-static u32 hdmi_edid_get_sink_mode(void *input)
+u32 hdmi_edid_get_sink_mode(void *input, u32 mode)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
bool sink_mode;
@@ -2419,8 +2431,13 @@ static u32 hdmi_edid_get_sink_mode(void *input)
if (edid_ctrl->edid_override &&
(edid_ctrl->override_data.sink_mode != -1))
sink_mode = edid_ctrl->override_data.sink_mode;
- else
- sink_mode = edid_ctrl->sink_mode;
+ else {
+ if (edid_ctrl->sink_mode &&
+ (mode > 0 && mode <= HDMI_EVFRMT_END))
+ sink_mode = SINK_MODE_HDMI;
+ else
+ sink_mode = SINK_MODE_DVI;
+ }
return sink_mode;
} /* hdmi_edid_get_sink_mode */
@@ -2435,10 +2452,21 @@ static u32 hdmi_edid_get_sink_mode(void *input)
*/
bool hdmi_edid_is_dvi_mode(void *input)
{
- if (hdmi_edid_get_sink_mode(input))
- return false;
- else
+ struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+ int sink_mode;
+
+ if (!edid_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
return true;
+ }
+
+ if (edid_ctrl->edid_override &&
+ (edid_ctrl->override_data.sink_mode != -1))
+ sink_mode = edid_ctrl->override_data.sink_mode;
+ else
+ sink_mode = edid_ctrl->sink_mode;
+
+ return (sink_mode == SINK_MODE_DVI);
}
/**
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
index af802bb45f89..c604d0fbf7b2 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
@@ -58,10 +58,16 @@ struct hdmi_edid_override_data {
int vic;
};
+enum edid_sink_mode {
+ SINK_MODE_DVI,
+ SINK_MODE_HDMI
+};
+
int hdmi_edid_parser(void *edid_ctrl);
u32 hdmi_edid_get_raw_data(void *edid_ctrl, u8 *buf, u32 size);
u8 hdmi_edid_get_sink_scaninfo(void *edid_ctrl, u32 resolution);
bool hdmi_edid_is_dvi_mode(void *input);
+u32 hdmi_edid_get_sink_mode(void *edid_ctrl, u32 mode);
bool hdmi_edid_sink_scramble_override(void *input);
bool hdmi_edid_get_sink_scrambler_support(void *input);
bool hdmi_edid_get_scdc_support(void *input);
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index 07592fa26a49..2e267f2695d7 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -66,7 +66,7 @@
#define HDMI_TX_4_MAX_PCLK_RATE 600000
#define hdmi_tx_get_fd(x) ((x && (ffs(x) > 0)) ? \
- hdmi_ctrl->feature_data[ffs(x) - 1] : 0)
+ hdmi_ctrl->feature_data[ffs(x) - 1] : NULL)
#define hdmi_tx_set_fd(x, y) {if (x && (ffs(x) > 0)) \
hdmi_ctrl->feature_data[ffs(x) - 1] = y; }
@@ -375,9 +375,12 @@ static void hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
}
}
-static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl)
+static inline bool hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl)
{
- return hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID));
+ void *data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID);
+
+ return (hdmi_edid_get_sink_mode(data,
+ hdmi_ctrl->vic) == SINK_MODE_DVI);
} /* hdmi_tx_is_dvi_mode */
static inline u32 hdmi_tx_is_in_splash(struct hdmi_tx_ctrl *hdmi_ctrl)
@@ -578,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;
@@ -630,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);
@@ -2156,6 +2165,8 @@ static int hdmi_tx_init_panel_info(struct hdmi_tx_ctrl *hdmi_ctrl)
pinfo->lcdc.v_front_porch = timing.front_porch_v;
pinfo->lcdc.v_pulse_width = timing.pulse_width_v;
pinfo->lcdc.frame_rate = timing.refresh_rate;
+ pinfo->lcdc.h_polarity = timing.active_low_h;
+ pinfo->lcdc.v_polarity = timing.active_low_v;
pinfo->type = DTV_PANEL;
pinfo->pdest = DISPLAY_3;
@@ -2442,6 +2453,7 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on)
struct dss_io_data *io = NULL;
/* Defaults: Disable block, HDMI mode */
u32 hdmi_ctrl_reg = BIT(1);
+ void *data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID);
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -2470,7 +2482,8 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on)
hdmi_ctrl_reg |= BIT(2);
/* Set transmission mode to DVI based in EDID info */
- if (hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)))
+ if (hdmi_edid_get_sink_mode(data,
+ hdmi_ctrl->vic) == SINK_MODE_DVI)
hdmi_ctrl_reg &= ~BIT(1); /* DVI mode */
/*
@@ -2929,7 +2942,6 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev,
{
int rc = 0;
struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev);
- u32 is_mode_dvi;
if (!hdmi_ctrl || !params) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -2938,9 +2950,8 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev,
mutex_lock(&hdmi_ctrl->tx_lock);
- is_mode_dvi = hdmi_tx_is_dvi_mode(hdmi_ctrl);
-
- if (!is_mode_dvi && hdmi_tx_is_panel_on(hdmi_ctrl)) {
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) &&
+ hdmi_tx_is_panel_on(hdmi_ctrl)) {
memcpy(&hdmi_ctrl->audio_params, params,
sizeof(struct msm_ext_disp_audio_setup_params));
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c
index 102c2f994646..827013d06412 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c
@@ -857,7 +857,7 @@ static int hdmi_ddc_read_retry(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
u32 reg_val, ndx, time_out_count, wait_time;
struct hdmi_tx_ddc_data *ddc_data;
int status;
- int busy_wait_us;
+ int busy_wait_us = 0;
if (!ddc_ctrl || !ddc_ctrl->io) {
pr_err("invalid input\n");
@@ -1335,7 +1335,7 @@ int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
u32 time_out_count;
struct hdmi_tx_ddc_data *ddc_data;
u32 wait_time;
- int busy_wait_us;
+ int busy_wait_us = 0;
if (!ddc_ctrl || !ddc_ctrl->io) {
pr_err("invalid input\n");
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index d88d87bd2092..6936c4c1f3cc 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -2180,7 +2180,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdss_set_quirk(mdata, MDSS_QUIRK_MDP_CLK_SET_RATE);
mdata->has_wb_ubwc = true;
set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map);
- set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_SEC_DETACH_SMMU, mdata->mdss_caps_map);
mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED);
break;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index cd403f19c088..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 {
@@ -1107,9 +1108,9 @@ static inline enum mdss_mdp_pu_type mdss_mdp_get_pu_type(
if (!is_split_lm(mctl->mfd) || mdss_mdp_is_both_lm_valid(mctl))
pu_type = MDSS_MDP_DEFAULT_UPDATE;
- else if (mctl->mixer_left->valid_roi)
+ else if (mctl->mixer_left && mctl->mixer_left->valid_roi)
pu_type = MDSS_MDP_LEFT_ONLY_UPDATE;
- else if (mctl->mixer_right->valid_roi)
+ else if (mctl->mixer_right && mctl->mixer_right->valid_roi)
pu_type = MDSS_MDP_RIGHT_ONLY_UPDATE;
else
pr_err("%s: invalid pu_type\n", __func__);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 929eeb270f32..efd681a5d954 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -571,6 +571,9 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe,
u64 ver_dwnscale;
u64 active_line;
u64 backfill_line;
+ struct mdss_mdp_ctl *ctl = pipe->mixer_left->ctl;
+ u64 pclk_rate;
+ struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
ver_dwnscale = (u64)src_h << PHASE_STEP_SHIFT;
do_div(ver_dwnscale, dst.h);
@@ -596,12 +599,26 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe,
total_cycle = active_line_cycle + backfill_cycle;
+ /*
+ * MDP clkrate = total_cycle * PixelClock / Dest-width
+ * if pixelClock not available:
+ * = total_cycle * fps * v_total
+ */
+ if ((pinfo->type == MIPI_CMD_PANEL) && dst.w) {
+ pclk_rate = (u64)mdss_panel_get_htotal(pinfo, false) *
+ v_total * fps;
+ do_div(pclk_rate, pinfo->xres);
+ total_cycle *= pclk_rate;
+ } else {
+ total_cycle *= (fps * v_total);
+ }
+
pr_debug("line: active=%lld backfill=%lld vds=%lld\n",
active_line, backfill_line, ver_dwnscale);
pr_debug("cycle: total=%lld active=%lld backfill=%lld\n",
total_cycle, active_line_cycle, backfill_cycle);
- return (u32)total_cycle * (fps * v_total);
+ return (u32)total_cycle;
}
static inline bool __is_vert_downscaling(u32 src_h,
@@ -659,6 +676,7 @@ static u32 get_pipe_mdp_clk_rate(struct mdss_mdp_pipe *pipe,
if (flags & PERF_CALC_PIPE_APPLY_CLK_FUDGE)
rate = mdss_mdp_clk_fudge_factor(mixer, rate);
+ rate = min(mdata->max_mdp_clk_rate, rate);
return rate;
}
@@ -2495,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)
@@ -2508,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) ||
@@ -2523,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_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index 587150bbc9fa..d9aaac4526ea 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -58,6 +58,8 @@ struct intf_timing_params {
u32 v_front_porch;
u32 hsync_pulse_width;
u32 vsync_pulse_width;
+ u32 h_polarity;
+ u32 v_polarity;
u32 border_clr;
u32 underflow_clr;
@@ -641,13 +643,8 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl,
display_hctl = (hsync_end_x << 16) | hsync_start_x;
den_polarity = 0;
- if (MDSS_INTF_HDMI == ctx->intf_type) {
- hsync_polarity = p->yres >= 720 ? 0 : 1;
- vsync_polarity = p->yres >= 720 ? 0 : 1;
- } else {
- hsync_polarity = 0;
- vsync_polarity = 0;
- }
+ hsync_polarity = p->h_polarity;
+ vsync_polarity = p->v_polarity;
polarity_ctl = (den_polarity << 2) | /* DEN Polarity */
(vsync_polarity << 1) | /* VSYNC Polarity */
(hsync_polarity << 0); /* HSYNC Polarity */
@@ -2178,7 +2175,8 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
itp->width = dsc->pclk_per_line;
itp->xres = dsc->pclk_per_line;
}
-
+ itp->h_polarity = pinfo->lcdc.h_polarity;
+ itp->v_polarity = pinfo->lcdc.v_polarity;
itp->h_back_porch = pinfo->lcdc.h_back_porch;
itp->h_front_porch = pinfo->lcdc.h_front_porch;
itp->v_back_porch = pinfo->lcdc.v_back_porch;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index 472f1e8e8e3b..e44edb8fbea7 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,
@@ -1691,11 +1692,24 @@ static struct mdss_mdp_pipe *__find_and_move_cleanup_pipe(
enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe = NULL;
+ struct mdss_mdp_data *buf, *tmpbuf;
if (__find_pipe_in_list(&mdp5_data->pipes_destroy,
pipe_ndx, &pipe, rect_num)) {
pr_debug("reuse destroy pipe id:%d ndx:%d rect:%d\n",
pipe->num, pipe_ndx, rect_num);
+ /*
+ * Pipe is being moved from destroy list to used list.
+ * It is possible that the buffer which was attached to a pipe
+ * in destroy list hasn't been cleaned up yet. Mark the buffers
+ * as cleanup to make sure that they will be freed before the
+ * pipe is reused.
+ */
+ list_for_each_entry_safe(buf, tmpbuf, &pipe->buf_queue,
+ pipe_list) {
+ buf->state = MDP_BUF_STATE_CLEANUP;
+ list_del_init(&buf->pipe_list);
+ }
list_move(&pipe->list, &mdp5_data->pipes_used);
} else if (__find_pipe_in_list(&mdp5_data->pipes_cleanup,
pipe_ndx, &pipe, rect_num)) {
@@ -1812,13 +1826,16 @@ static int __validate_secure_session(struct mdss_overlay_private *mdp5_data)
pr_debug("pipe count:: secure display:%d non-secure:%d secure-vid:%d,secure-cam:%d\n",
sd_pipes, nonsd_pipes, secure_vid_pipes, secure_cam_pipes);
+ MDSS_XLOG(mdss_get_sd_client_cnt(), sd_pipes, nonsd_pipes,
+ secure_vid_pipes, secure_cam_pipes);
if (mdss_get_sd_client_cnt() && !mdp5_data->sd_enabled) {
pr_err("Secure session already enabled for other client\n");
return -EINVAL;
}
- if ((sd_pipes) &&
+ if (((sd_pipes) || (mdp5_data->ctl->is_video_mode &&
+ mdss_get_sd_client_cnt())) &&
(nonsd_pipes || secure_vid_pipes ||
secure_cam_pipes)) {
pr_err("non-secure layer validation request during secure display session\n");
@@ -2410,14 +2427,14 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
int layer_count = commit->input_layer_cnt;
u32 ds_mode = 0;
- struct mdss_mdp_pipe *pipe, *tmp, *left_blend_pipe;
+ struct mdss_mdp_pipe *pipe = NULL, *tmp, *left_blend_pipe;
struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_mdp_pipe *left_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_mdp_mixer *mixer = NULL;
- struct mdp_input_layer *layer, *layer_list;
+ struct mdp_input_layer *layer = NULL, *layer_list;
struct mdss_mdp_validate_info_t *validate_info_list = NULL;
bool is_single_layer = false, force_validate;
enum layer_pipe_q pipe_q_type;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 3c679877705d..953aeb95332b 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -2318,6 +2318,31 @@ set_roi:
}
/*
+ * For the pipe which is being used for Secure Display,
+ * cleanup the previously queued buffers.
+ */
+static void __overlay_cleanup_secure_pipe(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_data *buf, *tmpbuf;
+
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
+ if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
+ list_for_each_entry_safe(buf, tmpbuf, &pipe->buf_queue,
+ pipe_list) {
+ if (buf->state == MDP_BUF_STATE_ACTIVE) {
+ __pipe_buf_mark_cleanup(mfd, buf);
+ list_move(&buf->buf_list,
+ &mdp5_data->bufs_freelist);
+ mdss_mdp_overlay_buf_free(mfd, buf);
+ }
+ }
+ }
+ }
+}
+
+/*
* Check if there is any change in secure state and store it.
*/
static void __overlay_set_secure_transition_state(struct msm_fb_data_type *mfd)
@@ -2393,9 +2418,19 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
if (mdp5_data->secure_transition_state == SD_NON_SECURE_TO_SECURE) {
if (!mdss_get_sd_client_cnt()) {
MDSS_XLOG(0x11);
- /*wait for ping pong done */
- if (ctl->ops.wait_pingpong)
+ /* wait for ping pong done */
+ if (ctl->ops.wait_pingpong) {
mdss_mdp_display_wait4pingpong(ctl, true);
+
+ /*
+ * For command mode panels, there will not be
+ * a NULL commit preceding secure display. If
+ * a pipe is reused for secure display,
+ * cleanup buffers in the secure pipe before
+ * detaching IOMMU.
+ */
+ __overlay_cleanup_secure_pipe(mfd);
+ }
/*
* unmap the previous commit buffers before
* transitioning to secure state
@@ -4413,7 +4448,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 +4636,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 +4684,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 +5022,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.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index 50c26047185c..9c2b1d42bd35 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -2783,7 +2783,7 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl,
(ctl->mfd->panel_info->type != WRITEBACK_PANEL));
if (valid_mixers && (mixer_cnt <= mdata->nmax_concurrent_ad_hw) &&
- valid_ad_panel) {
+ valid_ad_panel && (info->pp_program_mask & PP_PROGRAM_AD)) {
ret = mdss_mdp_ad_setup(ctl->mfd);
if (ret < 0)
pr_warn("ad_setup(disp%d) returns %d\n", disp_num, ret);
@@ -3264,6 +3264,8 @@ int mdss_mdp_pp_overlay_init(struct msm_fb_data_type *mfd)
pr_err("Invalid mfd %pK mdata %pK\n", mfd, mdata);
return -EPERM;
}
+ if (mfd->index >= (MDP_BLOCK_MAX - MDP_LOGICAL_BLOCK_DISP_0))
+ return 0;
if (mdata->nad_cfgs)
mfd->mdp.ad_calc_bl = pp_ad_calc_bl;
@@ -7641,6 +7643,13 @@ static int pp_mfd_release_all(struct msm_fb_data_type *mfd)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
int ret = 0;
+ if (!mfd || !mdata) {
+ pr_err("Invalid mfd %pK mdata %pK\n", mfd, mdata);
+ return -EPERM;
+ }
+
+ if (mfd->index >= (MDP_BLOCK_MAX - MDP_LOGICAL_BLOCK_DISP_0))
+ return ret;
if (mdata->nad_cfgs) {
ret = pp_mfd_ad_release_all(mfd);
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_mdp_splash_logo.c b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
index 7cc94447efdc..d412a1c66e79 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-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
@@ -150,7 +150,7 @@ static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd)
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
- int rc, ret;
+ int ret;
/*
* iommu dynamic attach for following conditions.
@@ -167,26 +167,41 @@ static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd)
return -EPERM;
}
- rc = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE,
+ /*
+ * Putting handoff pending to false to ensure smmu attach happens
+ * with early flag attribute
+ */
+ mdata->handoff_pending = false;
+
+ ret = mdss_smmu_set_attribute(MDSS_IOMMU_DOMAIN_UNSECURE, EARLY_MAP, 1);
+ if (ret) {
+ pr_debug("mdss set attribute failed for early map\n");
+ goto end;
+ }
+
+ ret = mdss_iommu_ctrl(1);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("mdss iommu attach failed\n");
+ goto end;
+ }
+
+ ret = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE,
mdp5_data->splash_mem_addr,
mdp5_data->splash_mem_addr,
mdp5_data->splash_mem_size,
IOMMU_READ | IOMMU_NOEXEC);
- if (rc) {
- pr_debug("iommu memory mapping failed rc=%d\n", rc);
+ if (ret) {
+ pr_err("iommu memory mapping failed ret=%d\n", ret);
} else {
- ret = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(ret)) {
- pr_err("mdss iommu attach failed\n");
- mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE,
- mdp5_data->splash_mem_addr,
- mdp5_data->splash_mem_size);
- } else {
- mfd->splash_info.iommu_dynamic_attached = true;
- }
+ pr_debug("iommu map passed for PA=VA\n");
+ mfd->splash_info.iommu_dynamic_attached = true;
}
- return rc;
+ ret = mdss_smmu_set_attribute(MDSS_IOMMU_DOMAIN_UNSECURE, EARLY_MAP, 0);
+end:
+ mdata->handoff_pending = true;
+
+ return ret;
}
static void mdss_mdp_splash_unmap_splash_mem(struct msm_fb_data_type *mfd)
@@ -194,12 +209,10 @@ static void mdss_mdp_splash_unmap_splash_mem(struct msm_fb_data_type *mfd)
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
if (mfd->splash_info.iommu_dynamic_attached) {
-
mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE,
mdp5_data->splash_mem_addr,
mdp5_data->splash_mem_size);
mdss_iommu_ctrl(0);
-
mfd->splash_info.iommu_dynamic_attached = false;
}
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index 22656175edf8..1ae3d0ba4ec6 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -428,8 +428,8 @@ static int mdss_mdp_get_ubwc_plane_size(struct mdss_mdp_format_params *fmt,
if (fmt->format == MDP_Y_CBCR_H2V2_UBWC ||
fmt->format == MDP_Y_CBCR_H2V2_TP10_UBWC) {
- uint32_t y_stride_alignment, uv_stride_alignment;
- uint32_t y_height_alignment, uv_height_alignment;
+ uint32_t y_stride_alignment = 0, uv_stride_alignment = 0;
+ uint32_t y_height_alignment = 0, uv_height_alignment = 0;
uint32_t y_tile_width = fmt_ubwc->micro.tile_width;
uint32_t y_tile_height = fmt_ubwc->micro.tile_height;
uint32_t uv_tile_width = y_tile_width / 2;
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index 37b0ca7aa44b..e8255ff45726 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -406,9 +406,10 @@ struct lcd_panel_info {
/* Pad height */
u32 yres_pad;
u32 frame_rate;
+ u32 h_polarity;
+ u32 v_polarity;
};
-
/* DSI PHY configuration */
struct mdss_dsi_phy_ctrl {
char regulator[7]; /* 8996, 1 * 5 */
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index 399a12e3dcc8..61b0518d3ee6 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -687,7 +687,7 @@ static struct mdss_rot_hw_resource *mdss_rotator_hw_alloc(
struct mdss_rot_hw_resource *hw;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 pipe_ndx, offset = mdss_mdp_get_wb_ctl_support(mdata, true);
- int ret;
+ int ret = 0;
hw = devm_kzalloc(&mgr->pdev->dev, sizeof(struct mdss_rot_hw_resource),
GFP_KERNEL);
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 1b4765837c61..02e6d210b6e4 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -280,6 +280,27 @@ end:
return rc;
}
+int mdss_smmu_set_attribute(int domain, int flag, int val)
+{
+ int rc = 0, domain_attr = 0;
+ struct mdss_smmu_client *mdss_smmu = mdss_smmu_get_cb(domain);
+
+ if (!mdss_smmu) {
+ pr_err("not able to get smmu context\n");
+ return -EINVAL;
+ }
+
+ if (flag == EARLY_MAP)
+ domain_attr = DOMAIN_ATTR_EARLY_MAP;
+ else
+ goto end;
+
+ rc = iommu_domain_set_attr(mdss_smmu->mmu_mapping->domain,
+ domain_attr, &val);
+end:
+ return rc;
+}
+
/*
* mdss_smmu_attach_v2()
*
@@ -477,7 +498,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 +507,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..679e564bcbc9 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.h
+++ b/drivers/video/fbdev/msm/mdss_smmu.h
@@ -48,6 +48,11 @@ struct mdss_smmu_private {
void mdss_smmu_register(struct device *dev);
int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev);
+int mdss_smmu_set_attribute(int domain, int flag, int val);
+
+enum smmu_attributes {
+ EARLY_MAP
+};
static inline int mdss_smmu_dma_data_direction(int dir)
{
@@ -253,7 +258,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..8d1459088b80
--- /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%pK, cap = 0x%pK", 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);
+
+static 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%pK, v4l2_subdev 0x%pK",
+ __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: %pK", 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..1306fca46652
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_common.c
@@ -0,0 +1,647 @@
+/* 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,
+};
+
+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 = NULL;
+
+ if (!size || !inst)
+ return NULL;
+
+ cluster = kmalloc(sizeof(struct v4l2_ctrl *) *
+ BA_NUM_CTRLS, GFP_KERNEL);
+
+ if (!cluster)
+ 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;
+ int rc = 0;
+ int cluster_size = 0;
+
+ memset(&ctrl_cfg, 0x00, sizeof(struct v4l2_ctrl_config));
+
+ 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..b57f4c94e18b
--- /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%pK", 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%pK", 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 '%pKd/%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: %pK", dev_ctxt);
+ goto failed_create_dir;
+ }
+
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "dev_%pK", 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: %pK", 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: %pK (%s)", inst,
+ "BA device");
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "dev: %pK", 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: %pK", inst);
+ goto failed_create_dir;
+ }
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%pK", 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..89fc08dd3c33
--- /dev/null
+++ b/drivers/video/msm/ba/msm_v4l2_ba.c
@@ -0,0 +1,614 @@
+/*
+ * 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;
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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",
+ __func__, __LINE__);
+ 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 %pK 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;
+}
+
+static 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;
+}
+
+static 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/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
index df2e6f783318..527de56f832f 100644
--- a/drivers/xen/privcmd.c
+++ b/drivers/xen/privcmd.c
@@ -335,8 +335,8 @@ static int mmap_batch_fn(void *data, int nr, void *state)
st->global_error = 1;
}
}
- st->va += PAGE_SIZE * nr;
- st->index += nr;
+ st->va += XEN_PAGE_SIZE * nr;
+ st->index += nr / XEN_PFN_PER_PAGE;
return 0;
}